Таблица 4. Описание работы функции QueueUserWorkItem
Вызов функции при окончании асинхронной операции
ввода/вывода
Если в программе выполняется большое количество
операций ввода/вывода, лучше, чтобы они выполнялись асинхронно. Это намного
повышает производительность приложения и уменьшает время отклика на клиентские
запросы. Однако самое сложное в асинхронных операциях – правильно продолжить
или завершить их. Можно связать все устройства (файлы) с портом завершения и
обрабатывать окончания операций в отдельном потоке. Когда операций много, вновь
встает вопрос об организации пула потоков для обработки асинхронных операций
ввода/вывода. К счастью, у нас имеется хороший помощник:
BOOL BindIoCompletionCallback(
// хендл файла
HANDLE FileHandle,
// функция обработки завершения запроса
LPOVERLAPPED_COMPLETION_ROUTINE
Function,
// зарезервировано
ULONG Flags
);
|
Эта функция создает порт завершения и связывает его с
файлом. Затем функция создает поток, который сразу же начинает ждать
(GetQueuedCompletionStatus) окончания асинхронной операции, после чего вызывает
определяемую вами функцию для обработки запроса. Вот ее прототип:
VOID CALLBACK FileIOCompletionRoutine(
DWORD dwErrorCode, // код завершения
DWORD
dwNumberOfBytesTransfered, // количество переданных байтов
LPOVERLAPPED lpOverlapped // структура OVERLAPPED
);
|
Хотя прототип этой функции идентичен функции, вызываемой при окончании операций, начатых ReadFileEx и WriteFileEx, не стоит
их путать. При использовании BindIoCompletionCallback эта функция вызывается с
помощью порта завершения ввода/вывода, тогда как при использовании ReadFileEx и
WriteFileEx функция вызывается с помощью APC.
Совершенно непонятно, почему в Microsoft решили не
использовать флаги для этой функции, но факт остается фактом. И хотя Рихтер в
своей статье, которая упоминалась выше, утверждает, что можно указать флаг
WT_EXECUTEINIOTHREAD, это неправда. Вы можете сами посмотреть дизассемблером в
ntdll.dll, например, функцию RtlSetIoCompletionCallback и убедиться, что третий
параметр в ней просто не используется.
Как видно из прототипа, вы не можете передавать
какого-либо дополнительного параметра функции FileIOCompletionRoutine, что
может вызвать определенные проблемы. Самым распространенным решением является
передача в функцию начала асинхронной операции структуры, производной от
OVERLAPPED. Тогда в дополнительных членах структуры можно передавать какую
угодно информацию – сама структура OVERLAPPED не копируется, везде передается
только указатель на нее.