Программа 7.1. grepMT: многопоточный поиск текстового шаблона
/* Глава 7. grepMT. */
/* Параллельный поиск текстового шаблона — версия, использующая несколько потоков. */
#include "EvryThng.h"
typedef struct { /* Структура данных потока поиска. */
int argc;
TCHAR targv[4][МАХ_РАТН];
} GREP_THREAD_ARG;
typedef GREP_THREAD_ARG *PGR_ARGS;
static DWORD WINAPI ThGrep(PGR_ARGS pArgs);
int _tmain(int argc, LPTSTR argv[]) {
GREP_THREAD_ARG * gArg;
HANDLE * tHandle;
DWORD ThdIdxP, ThId, ExitCode;
TCHAR CmdLine[MAX_COMMAND_LINE];
int iThrd, ThdCnt;
STARTUPINFO Startup;
PROCESS_INFORMATION ProcessInfo;
GetStartupInfo(&StartUp);
/* Основной поток: создает отдельные потоки поиска на основе функции "grep" для каждого файла. */
tHandle = malloc((argc – 2) * sizeof(HANDLE));
gArg = malloc((argc – 2) * sizeof(GREP_THREAD_ARG));
for (iThrd = 0; iThrd < argc – 2; iThrd++) {
_tcscpy(gArg[iThrd].targv[1], argv[1]); /* Pattern. */
_tcscpy(gArg[iThrd].targv[2], argv[iThrd + 2]);
GetTempFileName /* Имя временного файла. */
(".", "Gre", 0, gArg[iThrd].targv[3]);
gArg[iThrd].argc = 4;
/* Создать рабочий поток для выполнения командной строки. */
tHandle[iThrd] = (HANDLE)_beginthreadex(NULL, 0, ThGrep, &gArg[iThrd], 0, &ThId);
}
/* Перенаправить стандартный вывод для вывода списка файлов. */
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, после замены в нем термина "процесс" на термин "поток", может служить графической иллюстрацией соответствующих отношений. Главный поток (основной поток в данном случае) поручает выполнение отдельных задач рабочим потокам. Каждый рабочий, поток получает файл, в котором она должна выполнить поиск, а полученные рабочим потоком результаты передаются главному потоку во временном файле.
Читать дальше