Common Intermediate Language и системное программирование в Microsoft.Net. Макаров А.В - 118 стр.

UptoLike

/* посылаем несколько запросов в порт */
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 предназна-