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

Использование потоков

Если вы не хотите, чтобы работа программного потока приостановилась для выполнения процедур ввода/вывода, создайте второй программный поток и поручите выполнение ввода/вывода ему. Тогда первый программный поток сможет продолжить работу в то время, как второй поток будет выполнять медлительные процедуры, связанные с вводом/выводом. Например, для реализации терминальной программы вы можете использовать два потока: один читает команды, вводимые с клавиатуры, а второй следит за поступлением команд через последовательный порт. Оба потока записывают любые принятые ими данные в циклический буфер. Третий поток проверяет содержимое каждого из буферов и выполняет действия в соответствии с поступающими командами. При этом скорость поступления данных через последовательный порт не повлияет на скорость обработки команд, вводимых с клавиатуры.

При реализации подобного простого подхода обычно возникает проблема: третий поток должен постоянно следить за изменением содержимого циклических буферов, чтобы вовремя обнаружить появление в любом из них нового символа. Таким образом, для его работы требуется дополнительное процессорное время. Более сложная реализация может предусматривать использование событий. Когда один из потоков ввода принимает символ, он переводит специально предназначенное для этого событие в сигнальное состояние. При этом третий поток может использовать для слежения за состоянием события функцию WaitForMultipleObjects. To есть он не будет расходовать слишком много процессорного времени. Для слежения за состоянием консоли можно использовать непосредственно дескриптор консоли. Этот дескриптор переходит в сигнальное состояние в момент, когда в потоке ввода консоли появляются данные, ожидающие чтения. Если вместо события программа следит за состоянием дескриптора консоли, можно ограничиться только двумя потоками.

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

Все перечисленные стратегии очень просты в реализации, однако они недостаточно эффективны. Создание потоков — относительно ресурсоемкая операция. Кроме того, максимальное количество потоков, одновременно работающих в системе, ограничено. Конечно, это не будет проблемой для простой консольной программы, однако, если ваше приложение должно поддерживать интенсивный асинхронный ввод/вывод, применение для этой цели большого количества потоков может оказаться неприемлемым.

Перекрывающийся ввод/вывод

Значительно более эффективным способом реализации асинхронного ввода/вывода в Windows является перекрывающийся ввод/вывод. В Windows эта разновидность ввода/вывода поддерживается в отношении фактически всех типов файлов. В частности, вы можете применить перекрывающийся ввод/вывод в отношении дисковых файлов, коммуникационных портов, именованных каналов (named pipes) и сетевых сокетов. В общем и целом, если в отношении чего-либо можно применить операции ReadFilе и WriteFile, значит, при этом допускается использовать перекрывающийся ввод/вывод.

Прежде чем вы сможете осуществлять в отношении файла операции перекрывающегося ввода/вывода, вы должны открыть файл при помощи вызова CreateFile с использованием флага FILE_FLAG_OVERLAPPED. Если этот флаг не указать, перекрывающийся ввод/вывод будет невозможен. Если флаг установлен, вы не сможете использовать файл обычным способом. При обмене данными с файлом будет использоваться перекрывающийся ввод/вывод.