Асинхронный файловый ввод/вывод в операционной системе WINDOWS, страница 12

Основная программа входит в цикл, читая данные из канала все время, пока она получает ошибки ERROR_PIPE_LISTENING. Эти ошибки свидетельствуют о том, что канал все еще не соединен с другим процессом. В конце концов, либо в канале появляется какой-либо символ (в этом случае программа печатает его на консоли), либо программа узнает, что операция ввода/вывода находится в стадии выполнения (об этом свидетельствует ошибка ERROR_IO_PENDING). В последнем случае по завершении операции событие pipeevent перейдет в сигнальное состояние. Чтобы следить одновременно за состоянием события pipeevent и за состоянием дескриптора консоли, программа входит в режим ожидания, который инициируется вызовом WaitForMultipleObjects.

Когда ожидание завершается, программа определяет, что стало причиной его завершения. Если в сигнальное состояние перешел дескриптор консоли, программа читает данные с консоли (для этого используется вызов ReadConsoleInput) и пересылает эти данные в канал. Чтобы не усложнять программу, в нее не включен  код, отслеживающий несколько последовательных операций записи в канал в режиме перекрывающегося ввода/вывода. Если операция записи в канал не завершилась успехом сразу же после ее инициализации, программа просто обращается к функции GetOverlappedResult и ждет, пока вывод в канал успешно завершится. Нетрудно понять, что при этом асинхронный вывод фактически превращается в синхронный. Однако запись в канал обычно выполняется достаточно быстро, поэтому для данной программы такой подход не станет причиной проблем.

Если завершение работы функции WaitForMultipleObjects было вызвано переходом в сигнальное состояние события pipeevent, программа обращается к функции GetOverlappedResult, чтобы проверить наличие каких-либо ошибок. Если ошибок нет, программа выводит данные на консоль (для этого не требуется применять асинхронный ввод/вывод). После этого программа вновь обращается к функции ReadFilе для того, чтобы прочитать данные из канала.

Обратите внимание, что в этот момент канал может уже содержать данные для чтения. В этом случае программа должна прочитать эти данные и только потом инициировать операцию чтения канала. По этой причине код печати символа расположен внутри цикла do:

do

{ if (pipebuf==”\r”) cout<<”\n”; else cout<<pipebuf;

} while (ReadFile(pipe, &pipebuf, 1, &inlen, &over));

Первый раз войдя в этот цикл, программа обрабатывает данные, полученные в результате асинхронного чтения, которое выполнялось до этого. Цикл выполняется более одного раза только в случае, если функция ReadFile возвращает TRUE, то есть данные получены из канала незамедлительно. Если в момент обращения к ReadFile данные в канале отсутствуют, этот вызов возвращает FALSE, завершая работу цикла. В этот момент необходимо обратиться к GetLastError для того, чтобы определить, возникла ли ошибка, или вызов ReadFile успешно инициировал операцию чтения, которая пока что не завершена (об этом свидетельствует значение ERROR_IO_PENDING).

Пример с использованием порта завершения

В листинге 3 приведен исходный код программы, которая выполняет те же функции, что и программа из листинга 2. В отличие от предыдущей программы программа из листинга 3 использует порт завершения. В данном конкретном случае можно обойтись и без портов завершения, однако вы можете усовершенствовать программу таким образом, чтобы во время ожидания ввода она не простаивала, выполняла какие-либо полезные действия.

На самом деле использовать для реализации подобной программы порты завершения не очень удобно. Почему? Рассмотрим код листинга 2. Если данные для чтения отсутствуют как в канале, так и на консоли, программа переходит в режим ожидания при помощи вызова WaitForMultipleObjects. В это время операционная система может выполнять любую другую работу. Программа будет находиться в режиме ожидания до тех пор, пока данные не появятся либо в канале, либо на консоли.