Джонсон Харт - Системное программирование в среде Windows
Если оба эти условия удовлетворены, выполняются процедуры завершения, помещенные в очередь в результате завершения операций ввода/вывода. Процедуры завершения выполняются в том же потоке, который выполнил первоначальный вызов функции ввода/вывода и находится в состоянии дежурного ожидания. Поэтому поток должен переходить в состояние дежурного ожидания только тогда, когда для выполнения процедур завершения существуют безопасные условия.
Функции дежурного ожидания
Всего предусмотрено пять функций дежурного ожидания, но ниже приводятся прототипы только трех из них, которые представляют для нас непосредственный интерес:
DWORD WaitForSingleObjectEx(HANDLE hObject, DWORD dwMilliseconds, BOOL bAlertable)
DWORD WaitForMultipleObjectsEx(DWORD cObjects, LPHANDLE lphObjects, BOOL fWaitAll, DWORD dwMilliseconds, BOOL bAlertable)
DWORD SleepEx(DWORD dwMilliseconds, BOOL bAlertable)
В каждой из функций дежурного ожидания имеется флаг bAlertable, который в случае асинхронного ввода/вывода должен устанавливаться в TRUE. Приведенные выше функции являются расширением знакомых вам функций Wait и Sleep.
Длительность интервалов ожидания указывается, как обычно, в миллисекундах. Каждая из этих трех функций осуществляет возврат, как только наступает любая из перечисленных ниже ситуаций:
• Дескриптор (дескрипторы) переходит (переходят) в сигнальное состояние, чем удовлетворяются стандартные требования двух из функций ожидания.
• Истекает интервал ожидания.
• Все процедуры завершения, находящиеся в очереди потока, прекращают свое выполнение, а значение параметра bAlertable равно TRUE. Процедура завершения помещается в очередь тогда, когда завершается соответствующая ей операция ввода/вывода (рис. 14.2).
Заметьте, что со структурами OVERLAPPED в функциях ReadFileEx и WriteFileEx не связаны никакие события, поэтому ни один из дескрипторов, указываемых при вызове функции ожидания, не связывается непосредственно с какой-либо определенной операцией ввода/вывода. В то же время, функция SleepEx не связана с объектами синхронизации, и поэтому ее проще всего использовать. В случае функции SleepEx в качестве длительности интервала ожидания обычно указывают значение INFINITE, поэтому возврат из этой функции произойдет только после того, как закончится выполнение одной или нескольких процедур завершения, которые в настоящий момент находятся в очереди.
Выполнение процедуры завершения и возврат из функции дежурного ожидания
По окончании выполнения операции расширенного ввода/вывода связанная с ней процедура завершения со своими аргументами, определяющими структуру OVERLAPPED, счетчик байтов и код ошибки, помещается в очередь для выполнения.
Все процедуры завершения, находящиеся в очереди потока, начинают выполняться тогда, когда поток переходит в состояние дежурного ожидания. Они выполняются поочередно, но не обязательно в той же последовательности, в которой завершились операции ввода/вывода. Возврат из функции дежурного ожидания происходит только после того, как осуществят возврат процедуры завершения. Эту особенность важно учитывать для обеспечения правильного функционирования большинства программ, поскольку при этом предполагается, что процедуры завершения получают возможность подготовиться к очередному использованию структуры OVERLAPPED и выполнить другие необходимые действия для перевода программы в известное состояние, прежде чем будет осуществлен возврат из состояния дежурного ожидания.
Если возврат из функции SleepEx обусловлен выполнением одной или нескольких процедур завершения, находящихся в очереди, то возвращаемым значением функции будет WAIT_TO_COMPLETION, и это же значение будет возвращено функцией GetLastError, вызванной после выполнения возврата одной из функций ожидания.
В заключение отметим два момента:
1. При вызове любой из функций дежурного ожидания в качестве значения параметра интервала ожидания используйте INFINITE. В отсутствие возможности истечения интервала ожидания возврат из функций будет осуществляться лишь после того, как закончится выполнение всех процедур завершения или дескрипторы перейдут в сигнальное состояние.
2. Для передачи информации процедуре завершения общепринято использовать элемент данных hEvent структуры OVERLAPPED, поскольку это поле игнорируется ОС.
Взаимодействие между основным потоком, процедурами завершения и функциями дежурного ожидания иллюстрирует рис. 14.2. В этом примере запускаются три параллельные операции чтения, две из которых завершаются к тому моменту, когда начинается выполнение дежурного ожидания.
Рис. 14.2. Асинхронный ввод/вывод с использованием процедур завершения
Пример: преобразование файла с использованием расширенного ввода/вывода
Программа 14.3 (atouEX) представляет собой переработанную версию программы 14.1. Эти программы иллюстрируют различие между двумя методами асинхронного ввода/вывода. Программа atouEx аналогична программе 14.1, но большая часть кода, предназначенного для упорядочения ресурсов, перемещена в ней в процедуру завершения, а многие переменные сделаны глобальными, чтобы процедура завершения могла иметь к ним доступ. Вместе с тем, в приложении В показано, что в отношении быстродействия программа atouEx вполне может конкурировать с другими методами, в которых не используется отображение файлов, тогда как программа atouOV работает медленнее.
Программа 14.2. atouEx: преобразование файла с использованием расширенного ввода/вывода/* Глава 14. atouEX
Преобразование файла из ASCII в Unicode средствами РАСШИРЕННОГО ВВОДА/ВЫВОДА. */
/* atouEX файл1 файл2 */
#include "EvryThng.h"
#define MAX_OVRLP 4
#define REC_SIZE 8096 /* Размер блока не имеет столь важного значения в отношении производительности, как в случае atouOV. */
#define UREC_SIZE 2 * REC_SIZE
static VOID WINAPI ReadDone(DWORD, DWORD, LPOVERLAPPED);
static VOID WINAPI WriteDone(DWORD, DWORD, LPOVERLAPPED);
/* Первая структура OVERLAPPED предназначена для чтения, а вторая — для записи. Структуры и буферы распределяются для каждой предстоящей операции. */
OVERLAPPED OverLapIn[MAX_OVRLP], OverLapOut [MAX_OVRLP];
CHAR AsRec[MAX_OVRLP][REC_SIZE];
WCHAR UnRec[MAX_OVRLP][REC_SIZE];
HANDLE hInputFile, hOutputFile;
LONGLONG nRecord, nDone;
LARGE_INTEGER FileSize;
int _tmain(int argc, LPTSTR argv[]) {
DWORD ic;
LARGE_INTEGER CurPosIn;
hInputFile = CreateFile(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
hOutputFile = CreateFile(argv[2], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_OVERLAPPED, NULL);
FileSize.LowPart = GetFileSize(hInputFile, &FileSize.HighPart);
nRecord = FileSize.QuadPart / REC_SIZE;
if ((FileSize.QuadPart % REC_SIZE) != 0) nRecord++;
CurPosIn.QuadPart = 0;
for (ic = 0; ic < MAX_OVRLP; ic++) {
OverLapIn[ic].hEvent = (HANDLE)ic; /* Перегрузить событие. */
OverLapOut[ic].hEvent = (HANDLE)ic; /* Поля. */
OverLapIn[ic].Offset = CurPosIn.LowPart;
OverLapIn[ic].OffsetHigh = CurPosIn.HighPart;
if (CurPosIn.QuadPart < FileSize.QuadPart) ReadFileEx(hInputFile, AsRec[ic], REC_SIZE, &OverLapIn [ic], ReadDone);
CurPosIn.QuadPart += (LONGLONG)REC_SIZE;
}
/* Выполняются все операции чтения. Войти в состояние дежурного ожидания и оставаться в нем до тех пор, пока не будут обработаны все записи.*/
nDone = 0;
while (nDone < 2 * nRecord) SleepEx(INFINITE, TRUE);
CloseHandle(hInputFile);
CloseHandle(hOutputFile);
_tprintf(_T("Преобразование из ASCII в Unicode завершено.n"));
return 0;
}
static VOID WINAPI ReadDone(DWORD Code, DWORD nBytes, LPOVERLAPPED pOv) {
/* Чтение завершено. Преобразовать данные и инициировать запись. */
LARGE_INTEGER CurPosIn, CurPosOut;
DWORD ic, i;
nDone++;
/* Обработать запись и инициировать операцию записи. */
ic = (DWORD)(pOv->hEvent);
CurPosIn.LowPart = OverLapIn[ic].Offset;
CurPosIn.HighPart = OverLapIn[ic].OffsetHigh;
CurPosOut.QuadPart = (CurPosIn.QuadPart / REC_SIZE) * UREC_SIZE;
OverLapOut[ic].Offset = CurPosOut.LowPart;
OverLapOut[ic].OffsetHigh = CurPosOut.HighPart;
/* Преобразовать запись из ASCII в Unicode. */
for (i = 0; i < nBytes; i++) UnRec[ic][i] = AsRec[ic][i];
WriteFileEx(hOutputFile, UnRec[ic], nBytes*2, &OverLapOut[ic], WriteDone);
/* Подготовить структуру OVERLAPPED для следующего чтения. */
CurPosIn.QuadPart += REC_SIZE * (LONGLONG)(MAX_OVRLP);
OverLapIn[ic].Offset = CurPosIn.LowPart;
OverLapIn[ic].OffsetHigh = CurPosIn.HighPart;
return;
}
static VOID WINAPI WriteDone(DWORD Code, DWORD nBytes, LPOVERLAPPED pOv) {
/* Запись завершена. Инициировать следующую операцию чтения. */
LARGE_INTECER CurPosIn;
DWORD ic;
nDone++;
ic = (DWORD)(pOv->hEvent);
CurPosIn.LowPart = OverLapIn[ic].Offset;
CurPosIn.HighPart = OverLapIn[ic].OffsetHigh;
if (CurPosIn.QuadPart < FileSize.QuadPart) {