Сценарий 4; вызов функций по завершении запросов на асинхронный ввод-вывод
Последний сценарий самый распространенный. Ваше серверное приложение выдает запросы на асинхронный ввод-вывод, и Вам нужен пул потоков, готовых к их обра ботке. Это как раз тот случай, на который и были изначально рассчитаны порты за вершения ввода-вывода Если бы Вы управляли собственным пулом потоков, Вы со здали бы порт завершения ввода-вывода и пул потоков, ждущих на этом порте Kpo мс того, Вы открыли бы пару-тройку устройств ввода-вывода и связали бы их описа тели с портом. По мерс завершения асинхронных запросов на ввод-вывод, драйверы устройств помещали бы "рабочие элементы" в очередь порта завершения.
Это прекрасная архитектура, позволяющая небольшому количеству потоков эф фективно обрабатывать несколько рабочих элементов, и очень хорошо, что она за
ложена в функции пуля потоков. Благодаря этому Вы сэкономите уйму времени и сил. Для использования преимуществ данной архитектуры надо лишь открыть требуемое устройство и сопоставить его с компонентом поддержки других операций (не свя занных с вводом-выводом) Учтите, что все потоки в этом компоненте ждут на порте завершения Чтобы сопоставить устройство с компонентом поддержки других опе раций, вызовите функцию:
BOOL BindIoCompletionCallback( HANDLE hDevice, POVERLAPPED_COMPLETION_ROUTINE pfnCallback, ULONG dwFlags);
Эта функция обращается к CreateIoCompletionPort, передавая eй hDevice и описа тель внутреннего порта завершения. Ее вызов также гарантирует, что в компоненте поддержки других операций есть хотя бы один поток Ключ завершения, сопостав ленный с устройством, — это адрес перекрывающейся подпрограммы завершения Так что, когда ввод-вывод на устройство завершается, компонент пула уже знает, какую функцию надо вызвать для обработки завершенного запроса. У подпрограммы завер шения должен быть следующий прототип:
VOID WINAPI OverlappedCompletionRoutine( DWORD dwErrorCode, DWORD dwNumberOfBytesTransfeгred, POVERLAPPED pOverlapped);
Заметьте, структура OVERLAPPED передается не в BindIoCompletionCallback, а в функции типа ReadFile и WriteFile. Сиосма внутренне отслеживает эту структуру вме сте с запросом на ввод-вывод.
После его завершения система поместит адрес струк туры в порт завершения для последующей передачи Вашей OverlappedCompletion Routine А поскольку адрес подпрограммы завершения — это и ключ завершения, то для передачи дополнительной контекстной информации в OverlappedCompletion Routine Вы должны прибегнуть к традиционному трюку и разместить эту информа цию в конце структуры OVERLAPPED.
Также учтите, что закрытие устройства приводит к немедленному завершению всех текущих запросов на ввод-вывод и дает ошибку Будьте готовы к этому в своей функ ции обратного вызова Если Вы хотите, чтобы после закрытия устройства функции обратного вызова больше не выполнялись, создайте в своем приложении контроль ный счетчик. При выдаче запроса на ввод-вывод Вы будете увеличивать его значение на 1, а при завершении — соответственно уменьшать.
Каких-то специальных флагов для функции BindloComplettonCallback сейчас не предусматривается, поэтому Вы должны передавать 0 в параметре dwFlags. Но, по моему, один флаг, WT_EXECUTEINIOTHREAD, ей следовало бы поддерживать. После завершения запроса на ввод-вывод он заставил бы поместить этот запрос в очередь одного из потоков компонента поддержки других операций (не связанных с вводом выводом) Всдь OverlappedCompletionRoutine, вероятно, выдаст еще один запрос на асинхронный ввод-вывод. Однако, если поток завершается, всс выданные им запро сы на ввод-вывод автоматически уничтожаются Кроме того, надо учесть, что потоки в компоненте поддержки других операций создаются и уничтожаются в зависимости от текущей нагрузки. При низкой нагрузке поток может быть закрыт, оставив неза вершенные запросы. Если бы функция BtndIoCompletionCallback поддерживала флаг WT_EXECUTEINIOTHREAD, то поток, ждущий на порте завершения, мог бы пробудить ся и передать результат потоку компонента поддержки ввода-вывода И поскольку эти
потоки никогда не завершаются при наличии запросов, Вы могли бы выдавать такие запросы, не опасаясь потерять их
Флаг WT_EXECUTEINIOTHREAD был бы, конечно, очень удобен, но Вы можете легко эмулировать все то, о чем я сейчас говорил В своей функции OverlappedCompletionRoutme просто вызовите QueueUserWorkltem с флагом WT_EXECUTEINIOTHREAD и передайте нужные данные (наверное, как минимум, структуру OVERLAPPED) Ничего другого функции пула Вам и не предложили бы.