- •1. Введение
- •2. Многопоточное программирование
- •3.1. Example1: работа с памятью
- •3.1.1. Производительность
- •3.1.2. Масштабируемость
- •3.1.3. Масштабируемость Hyper-Threading архитектур
- •3.2. Example2.Exe: работа с файлами
- •4. Многопоточные программы
- •4.1. Mtftext.Exe: учебный пример
- •4.2. Mtcksrc.Exe: проверка исходного кода
- •If (tout.Size()) file(mp, fd::out).Write(tout);
- •4.3. Mtdel.Exe: удаление файлов
- •Вывод итоговой статистики, т.Е. Общего количества удаленных файлов и их совокупного размера.
- •Сообщение о возникших ошибках, как последнее сообщение программы.
- •4.4. Mtcnvsrc.Exe: конвертация исходного кода
- •4.5. Mtdirdiff.Exe: сравнение директорий
- •Mem_pool ownmp;
- •5. Библиотека derslib
- •6. Заключение
4.4. Mtcnvsrc.Exe: конвертация исходного кода
Программа предназначена для рекурсивной конвертации файлов исходного кода в DOS/UNIX формат и/или удаления пробелов в конце строки.
Параметры командной строки: mtcnvsrc [-nt2] [d|u][e] mask[,mask2...]
[-ntN] |
количество потоков, 2 по умолчанию |
[d] |
конвертировать в DOS формат |
[e] |
удалять пробелы в конце строки |
[u] |
конвертировать в UNIX формат |
mask[,mask2...] |
маски для поиска файлов, могут содержать * и ? символы |
Примеры использования: mtcnvsrc.exe ue *.h,*.hpp,*.c,*.cpp mtcnvsrc.exe -nt4 e *.?pp
Как можно видеть, прямым назначением программы является исправление ошибок, найденных mtcksrc. При этом, автоматически исправляется только то, что не требует человеческого вмешательства.
Ну а если говорить о сложности, то данная программа скорее является небольшим подготовительным этапом перед следующей, чем усложнением предыдущей. Единственный момент, на который можно обратить внимание -- это аргумент потока в виде более общей структуры Arg. На этот раз аргументы хранятся в векторе vector<sh_ptr<Arg> >, но это не приводит к описанным выше проблемам, т.к. в процессе их создания копируются и перераспределяются элементы sh_ptr<Arg>, а не сами тяжеловесные Arg структуры.
4.5. Mtdirdiff.Exe: сравнение директорий
Программа предназначена для рекурсивного сравнения директорий.
Параметры командной строки: mtdirdiff [-nt2] old_dir new_dir diff_dir
[-ntN] |
количество потоков, 2 по умолчанию |
old_dir |
директория со старыми файлами |
new_dir |
директория с новыми файлами |
diff_dir |
директория для копирования изменений в виде add, del и mod поддиректорий |
Примеры использования: mtdirdiff.exe src\mtprog.sav src\mtprog src\mtprog.diff
А здесь мы уже имеем дело с небольшой, но достаточно нетривиальной многопоточной программой, в процессе создания которой решалось несколько неочевидных задач проектирования.
Прежде всего отмечу, что это первая программа, многопоточная работа которой разбита на несколько этапов, т.е. функция thread_pool::exec() вызывается неоднократно:
sh_thread_pool tp=(clp.numThr>1) ? new_thread_pool(mp, clp.numThr-1) :
new_thread_pool(mp);
MainTask mt(clp.oldDir, clp.newDir, clp.diffDir);
sh_data_queue dq=new_data_queue(mp);
dq_vec dqv(1, dq.get());
MsgIO mio(mp, *dq);
{
FindFilesMsg m1(&mt.oldDir), m2(&mt.newDir);
mio.write(m1);
mio.write(m2);
}
tp->exec(mt, dqv);
{
CompareMsg m1(true), m2(false);
mio.write(m1);
mio.write(m2);
}
mt.argno=0;
tp->exec(mt, dqv);
И, как следствие, устройство функции proc_arg() немного усложнено:
void* MainTask::proc_arg()
{
assert(argno<=int(args.size()));
if (argno==int(args.size())) args.push_back(newArg(gmp));
return args[argno++].get();
}
Необходимость разбиения на этапы возникает из желания эффективным и естественным образом организовать сканирование и последующее сравнение old_dir и new_dir директорий:
Сначала не более двух рабочих потоков производят поиск файлов и директорий с занесением результатов в объекты mt.oldDir и mt.newDir соответственно. Дополнительное количество потоков не используется в силу того, что заведение отдельных объектов DirCont для каждого из них с последующим объединением содержимого заметно усложнит структуру программы, вряд ли существенно ускорив ее выполнение.
Кстати сказать, проверка данной гипотезы является неплохим упражнением для самостоятельного изучения MT программирования -- возьметесь?! Вот и мне тоже лень...
Затем стартует сравнение просканированных директорий, причем потокам не нужно синхронизировать свой доступ к hash_vec-рам с именам, т.к. их содержимое уже никем не изменяется.
Как можно видеть, без разбиения на этапы потокам пришлось бы синхронизировать свой доступ к hash_vec-рам, что неприемлемо с точки зрения правильного MT дизайна.