Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточное програмирование.doc
Скачиваний:
3
Добавлен:
14.11.2019
Размер:
332.8 Кб
Скачать
  1. Вывод итоговой статистики, т.Е. Общего количества удаленных файлов и их совокупного размера.

К несчастью, решение данной простой задачи не вызовет никакого труда даже у едва знакомого с многопоточным программированием кодировщика: всего-то и нужно, что создать один глобальный счетчик, да защитить его mutex-ом!

С другой стороны, толковым программистам уже известно чем чреваты глобальные объекты, защищенные mutex-ами, а некоторые даже знают о том, что грамотный MT дизайн категорически не приветствует подобного рода решений!

Концептуально правильное решение проблемы получения итоговой статистики состоит в том, что каждый из рабочих потоков ведет свою собственную личную статистику, которую он может изменять абсолютно независимо и параллельно. А итоговая статистика получается в конце работы программы путем сложения их всех -- и никаких тебе mutex-ов!

Для создания личных аргументов потока предназначена функция MainTask::proc_arg(). Она вызывается thread_pool-ом для получения указателей, которые им будут переданы как arg в MainTask::proc(). Функция MainTask::proc_arg() сохраняет созданные объекты в списке, который затем обрабатывается MainTask::getStat() для получения итоговой статистики.

Отмечу, что для хранения Stat аргументов потоков специально используется список list<Stat>, а не более привычный vector<Stat>. Дело в том, что в процессе добавления элементов в вектор используемая им память неоднократно перераспределяется, так что указатели на объекты Stat, ранее возвращенные функцией MainTask::proc_arg() инвалидируются. А воспользоваться вызовом vector::reserve() мы не можем в силу того, что MainTask ничего не должна знать о количестве рабочих потоков.

  1. Сообщение о возникших ошибках, как последнее сообщение программы.

А вот еще одна серьезная задача, когда пресловутого mutex-а, казалось бы, уж точно не избежать! Суть проблемы в том, что если сразу же выводить на экран возникающие в процессе работы ошибки, то они вполне могут затеряться в выводе других потоков, продолжающих обработку своих сообщений. Решением задачи является запись сообщения об ошибке в специальный глобальный буфер, чтобы после окончания работы MainTask::proc() функция main() смогла его вывести последним.

В этом случае действительно имеет смысл завести общий для всех потоков буфер MainTask::gerr, распределенный с помощью глобального пула MainTask::gmp, и разграничить к нему доступ посредством привычной блокировки. Мы, конечно, могли бы завести по буферу в каждом аргументе потока и объединить их в конце работы точно так же, как мы объединяем статистику, но в данном случае усложнение кода программы неоправданно, т.к. потокам не нужно постоянно обращаться к этим данным и никаких дополнительных накладных расходов из-за данной блокировки не возникает.

Как ни крути, но очевидное решение с mutex-ом напрашивается само-собой и избежать его нам поможет только наблюдение о том, что однопоточный вариант работы программы ни в каких mutex-ах не нуждается, и даже более того: у нас должна быть возможность слинковать его с обычными, однопоточными версиями библиотек, никаких ссылок на mutex-ы, очевидно, не приемлющих. Так что прямое использование mutex-а отпадает... Как же быть?

К счастью, решение есть и оно заключается в использовании интерфейса task_opers, передаваемого в виде аргумента to в функцию MainTask::proc() thread_pool-ом. Он предоставляет функцию invoke(), которая позволяет вызывать переданный указатель на функцию с применением необходимой блокировки, т.е. необходимый (или нежелательный!) mutex автоматически обеспечивается самим thread_pool-ом:

void MainTask::proc(mem_pool& mp, const dq_vec& dqv, void* arg, task_opers& to)

{

data_queue& dq=*dqv[0];

Stat& st=*static_cast<Stat*>(arg);

приводим аргумент потока к его настоящему типу Stat

for (;;) {

shException exc(mp, 0);

try {

for (MsgIO mio(mp, dq); ; ) {

sh_ptr<Msg> msg=mio.read();

if (!msg.get()) break;

switch (msg->getType()) {

case Msg::FindFiles: {

doFindFiles(mp, dq, st, msg->to<FindFilesMsg>());

break;

}

}

}

return;

}

catch (shException she) { exc=she; }

catch (...) { exc=recatchException(mp, _FLINE_); }

ErrData ed(gerr, toTextAll(exc));

to.invoke(addError, &ed);

заполняем ErrData и вызываем функцию addError() для добавления сообщения об ошибке

dq.set_intr(true);

break;

}

}