Startup.dwFlags = STARTF_USESTDHANDLES;
Startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
/* Выполняются все рабочие потоки. Ожидать их завершения. */
ThdCnt = argc – 2;
while (ThdCnt > 0) {
ThdIdxP = WaitForMultipleObjects(ThdCnt, tHandle, FALSE, INFINITE);
iThrd = (int)ThdIdxP – (int)WAIT_OBJECT_0;
GetExitCodeThread(tHandle [iThrd], &ExitCode);
CloseHandle(tHandle [iThrd]);
if (ExitCode ==0) { /* Шаблон найден. */
if (argc > 3) {
/* Вывести имя файла, если имеется несколько файлов. */
_tprintf(_T("\n**Результаты поиска – файл: %s\n"), gArg[iThrd].targv [2]);
fflush(stdout);
}
/* Использовать программу "cat" для перечисления результирующих файлов. */
_stprintf(CmdLine, _T("%s%s"), _Т("cat "), gArg [iThrd].targv[3]);
CreateProcess(NULL, CmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &StartUp, &ProcessInfo);
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
}
DeleteFile(gArg[iThrd].targv[3]);
/* Скорректировать массивы потоков и имен файлов. */
tHandle[iThrd] = tHandle[ThdCnt – 1];
_tcscpy(gArg[iThrd].targv[3], gArg[ThdCnt – 1].targv[3]);
_tcscpy(gArg[iThrd].targv[2], gArg[ThdCnt – 1].targv[2]);
ThdCnt--;
}
}
/* Прототип функции контекстного поиска:
static DWORD WINAPI ThGrep(PGR_ARGS pArgs){ } */
Потоки и производительность
Программы grepMP и grepMT по своей структуре и сложности сопоставимы друг с другом, однако, как и следовало ожидать, программа grepMT характеризуется более высокой производительностью, так как переключение между потоками осуществляется ядром намного эффективнее, чем переключение между процессами. В приложении В показано, что эти теоретические ожидания отвечают действительности, и это особенно заметно в тех случаях, когда файлы размещены на различных дисках. Оба варианта реализации способны работать в SMP-системах, существенно улучшая показатели производительности в терминах общего времени выполнения (истекшего времени); потоки, независимо от того, принадлежат ли они одному и тому же или разным процессам, параллельно выполняются на различных процессорах. Измеренное пользовательское время в действительности превышает общее время выполнения, поскольку рассчитывается в виде суммарной величины для всех процессоров.
В то же время, существует весьма распространенное заблуждение, суть которого состоит в том, что отмеченный параллелизм, независимо от того, касается ли он использования нескольких процессов, как в случае grepMP, или же применения нескольких потоков, как в случае grepMT, способен приводить к повышению производительности лишь в случае SMP-систем. Выигрыш в производительности можно получить и при использовании нескольких дисков, а также при любом другом распараллеливании в системе хранения. Во всех подобных случаях операции ввода/вывода с участием нескольких файлов будут осуществляться в параллельном режиме.
Модель "хозяин/рабочий" и другие модели многопоточных приложений
Программа grepMT демонстрирует модель многопоточных приложений, носящую название модели "хозяин/рабочий" ("boss/worker"), а рис. 6.3, после замены в нем термина "процесс" на термин "поток", может служить графической иллюстрацией соответствующих отношений. Главный поток (основной поток в данном случае) поручает выполнение отдельных задач рабочим потокам. Каждый рабочий, поток получает файл, в котором она должна выполнить поиск, а полученные рабочим потоком результаты передаются главному потоку во временном файле.