Составители:
Рубрика:
/* посылаем несколько запросов в порт */
for ( i = 0; i < MAXQUERIES; i++ ) {
PostQueuedCompletionStatus( hcport, 1, i, NULL );
Sleep( 60 );
}
Функция помещает в очередь запросов информацию о «как будто»
выполненной операции ввода-вывода, полностью повторяя аргументы –
такие как размер переданного блока данных, ключ завершения и указатель
на структуру OVERLAPPED, содержащую сведения об операции. Мы можем
передавать вместо этих значений произвольные данные. В данном приме-
ре, скажем, принято, что значение ключа завершения -1 совместно с дли-
ной переданного блока 0 означает необходимость завершить поток:
/* для завершения работы посылаем специальные запросы */
for ( i = 0; i < POOLSIZE; i++ ) {
PostQueuedCompletionStatus(hcport,0, (ULONG_PTR)-1,NULL);
}
/* дожидаемся завершения всех потоков пула
и закрываем описатели */
WaitForMultipleObjects( POOLSIZE, hthread, TRUE, INFINITE );
for ( i = 0; i < POOLSIZE; i++ ) CloseHandle( hthread[i] );
CloseHandle( hcport );
return 0;
}
В общем виде поток в пуле реализует цикл с выбором запросов из
очереди с помощью функции GetQueuedCompletionStatus. Следует внима-
тельно ознакомиться с описанием этой функции, чтобы грамотно обраба-
тывать возможные ошибочные ситуации и предусмотреть при необходи-
мости завершение работы потока.
В данном примере поток в течении 0.3 секунды просто ждет, то есть
не исполняется, и порт завершения может передать запросы всем потокам
пула, хотя их количество превышает максимальное число одновременно
работающих потоков, указанное при создании порта:
unsigned __stdcall PoolProc( void *arg )
{
DWORD size;
ULONG_PTR key;
LPOVERLAPPED lpov;
while (
GetQueuedCompletionStatus(
(HANDLE)arg, &size, &key, &lpov, INFINITE
222
CIL и системное программирование в Microsoft .NET
)) {
/* проверяем условия завершения цикла */
if ( !size && key == (ULONG_PTR)-1 ) break;
Sleep( 300 );
}
return 0L;
}
Рассмотренный механизм управления пулом потоков весьма эффек-
тивен, однако требует некоторого объема ручной работы по созданию пор-
та, по созданию пула потоков и по управлению этими потоками.
В современных реализациях Windows предусмотрена возможность ав-
томатического создания и управления пулом потоков с помощью функции
BOOL QueueUserWorkItem(
LPTHREAD_START_ROUTINE QueryFunction,
PVOID pContext, ULONG Flags
);
Эта функция при необходимости создает пул потоков (число потоков
в пуле определяется числом процессоров), создает порт завершения вво-
да-вывода и размещает в очереди порта запрос. Если нужный порт и пул
потоков уже созданы, то она просто размещает новый запрос в очереди
порта. При обработке запроса будет вызвана указанная параметром
QueryFunction процедура с аргументом pContext:
DWORD WINAPI QueryFunction( PVOID pContext )
{
...
return 0L;
}
Таким образом, управление пулом потоков сильно упрощается, хотя
при этом теряется возможность связывания порта завершения ввода-вы-
вода с конкретными файлами и все запросы должны размещаться в очере-
ди явным вызовом функции QueueUserWorkItem.
Есть и еще одна особенность у такого способа управления пулом –
явного механизма задания числа потоков в пуле не предусмотрено. Одна-
ко у разработчика есть возможность управлять этим процессом с помо-
щью последнего параметра функции, содержащего специфичные флаги.
Так, с помощью флага WT_EXECUTEDEFAULT запрос будет направлен обычно-
му потоку из пула, флаг WT_EXECUTEINIOTHREAD заставит систему обрабаты-
вать запрос в потоке, который находится в состоянии ожидания оповеще-
ния (то есть, надо предусмотреть явные вызовы функции типа SleepEx или
WaitForMultipleObjectsEx и т.д.). Флаг WT_EXECUTELONGFUNCTION предназна-
Разработка параллельных приложений для ОС Windows
223
222 CIL и системное программирование в Microsoft .NET Разработка параллельных приложений для ОС Windows 223 /* посылаем несколько запросов в порт */ )) { for ( i = 0; i < MAXQUERIES; i++ ) { /* проверяем условия завершения цикла */ PostQueuedCompletionStatus( hcport, 1, i, NULL ); if ( !size && key == (ULONG_PTR)-1 ) break; Sleep( 60 ); Sleep( 300 ); } } Функция помещает в очередь запросов информацию о «как будто» return 0L; выполненной операции ввода-вывода, полностью повторяя аргументы – } такие как размер переданного блока данных, ключ завершения и указатель Рассмотренный механизм управления пулом потоков весьма эффек- на структуру OVERLAPPED, содержащую сведения об операции. Мы можем тивен, однако требует некоторого объема ручной работы по созданию пор- передавать вместо этих значений произвольные данные. В данном приме- та, по созданию пула потоков и по управлению этими потоками. ре, скажем, принято, что значение ключа завершения -1 совместно с дли- В современных реализациях Windows предусмотрена возможность ав- ной переданного блока 0 означает необходимость завершить поток: томатического создания и управления пулом потоков с помощью функции /* для завершения работы посылаем специальные запросы */ BOOL QueueUserWorkItem( for ( i = 0; i < POOLSIZE; i++ ) { LPTHREAD_START_ROUTINE QueryFunction, PostQueuedCompletionStatus(hcport,0, (ULONG_PTR)-1,NULL); PVOID pContext, ULONG Flags } ); /* дожидаемся завершения всех потоков пула Эта функция при необходимости создает пул потоков (число потоков и закрываем описатели */ в пуле определяется числом процессоров), создает порт завершения вво- WaitForMultipleObjects( POOLSIZE, hthread, TRUE, INFINITE ); да-вывода и размещает в очереди порта запрос. Если нужный порт и пул for ( i = 0; i < POOLSIZE; i++ ) CloseHandle( hthread[i] ); потоков уже созданы, то она просто размещает новый запрос в очереди CloseHandle( hcport ); порта. При обработке запроса будет вызвана указанная параметром return 0; QueryFunction процедура с аргументом pContext: } В общем виде поток в пуле реализует цикл с выбором запросов из DWORD WINAPI QueryFunction( PVOID pContext ) очереди с помощью функции GetQueuedCompletionStatus. Следует внима- { тельно ознакомиться с описанием этой функции, чтобы грамотно обраба- ... тывать возможные ошибочные ситуации и предусмотреть при необходи- return 0L; мости завершение работы потока. } В данном примере поток в течении 0.3 секунды просто ждет, то есть Таким образом, управление пулом потоков сильно упрощается, хотя не исполняется, и порт завершения может передать запросы всем потокам при этом теряется возможность связывания порта завершения ввода-вы- пула, хотя их количество превышает максимальное число одновременно вода с конкретными файлами и все запросы должны размещаться в очере- работающих потоков, указанное при создании порта: ди явным вызовом функции QueueUserWorkItem. Есть и еще одна особенность у такого способа управления пулом – unsigned __stdcall PoolProc( void *arg ) явного механизма задания числа потоков в пуле не предусмотрено. Одна- { ко у разработчика есть возможность управлять этим процессом с помо- DWORD size; щью последнего параметра функции, содержащего специфичные флаги. ULONG_PTR key; Так, с помощью флага WT_EXECUTEDEFAULT запрос будет направлен обычно- LPOVERLAPPED lpov; му потоку из пула, флаг WT_EXECUTEINIOTHREAD заставит систему обрабаты- while ( вать запрос в потоке, который находится в состоянии ожидания оповеще- GetQueuedCompletionStatus( ния (то есть, надо предусмотреть явные вызовы функции типа SleepEx или (HANDLE)arg, &size, &key, &lpov, INFINITE WaitForMultipleObjectsEx и т.д.). Флаг WT_EXECUTELONGFUNCTION предназна-
Страницы
- « первая
- ‹ предыдущая
- …
- 116
- 117
- 118
- 119
- 120
- …
- следующая ›
- последняя »