Книга: Системное программирование в среде Windows

Пример: многопоточный поиск контекста

Пример: многопоточный поиск контекста

В программе 6.1 (grepMP) для выполнения одновременного поиска текстового шаблона в нескольких файлах использовались процессы. Программа 7.1 (grepMT), которая включает исходный код функции поиска текстового шаблона grep, обеспечивает выполнение поиска несколькими потоками в рамках одного процесса. Код функции поиска основан на вызовах функций файлового ввода/вывода библиотеки С. Основная программа аналогична той, которая предлагалась в варианте реализации, основанном на использовании процессов.

Этот пример также показывает, что применение потоков позволяет выполнять асинхронные операции ввода/вывода даже без привлечения специально для этого предназначенных методов, описанных в главе 14. В данном примере параллельным вводом/выводом с участием нескольких файлов управляет программа, в то время как основной или любого другого потока предоставляется возможность в ожидании завершения ввода/вывода выполнять дополнительную обработку. По мнению автора, способ реализации асинхронного ввода/вывода, обеспечиваемый потоками, является более простым, а сравнительный анализ эффективности различных методов, представленный в главе 14, поможет вам выработать собственное мнение на этот счет.

Мы увидим, однако, что в сочетании с портами завершения ввода/вывода операции асинхронного ввода/вывода становятся очень полезным, а часто и необходимым средством в тех случаях, когда количество потоков очень велико.

В иллюстративных целях в программу grepMT введено дополнительное отличие по сравнению с программой grepMP. В данном случае функция WaiForMultipleObjects ожидает завершения не всех потоков, а только одного. Соответствующая информация выводится без ожидания завершения других потоков. В большинстве случае порядок завершения потоков будет меняться от одного запуска программы к другому. Программу легко видоизменить таким образом, чтобы результаты отображались в порядке указания аргументов в командной строке; для этого будет достаточно сымитировать программу grepMP. 

Наконец, обратите внимание на ограничение в 64 потока, обусловленное значением константы MAXIMUM_WAIT_OBJECTS, которая ограничивает количество дескрипторов при вызове функции WaitForMultipleObjects. Если у вас возникнет необходимость в большем количестве потоков, организуйте для функций WaitForSingleObjects или WaitForMultipleObjects соответствующий цикл.

Предостережение

Программа grepMP осуществляет асинхронный ввод/вывод в том смысле, что отдельные потоки выполняют параллельное синхронное чтение различных файлов, которые блокируются до момента завершения операции чтения. Можно также организовать параллельное чтение одного и того же файла, если у него имеются различные дескрипторы (обычно, по одному дескриптору для каждого потока). Эти дескрипторы должны быть сгенерированы функцией CreateFile, а не функцией DuplicateHandle. В главе 14 описывается асинхронный ввод/вывод, осуществляемый как с использованием, так и без использования пользовательских потоков, а в примере, доступном на Web-сайте (программа atouMT, описанная в главе 14), операции ввода/вывода выполняются с использованием нескольких потоков по отношению к одному и тому же файлу.

Программа 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**Результаты поиска – файл: %sn"), 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){ } */

Оглавление книги


Генерация: 0.045. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз