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

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

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

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

Любой поток может получить информацию о завершении ввода/вывода при помощи вызова GetQueuedCompletionStatus. Этот вызов возвращает количество байт, переданных в процессе ввода/вывода, ключ завершения (32-битное число, установленное при обращении к CreateIoCompletion) и структуру OVERLAPPED, использованную при инициализации процедуры ввода/вывода. Как обычно, при обращении к GetQueuedCompletionStatus можно ожидать появления уведомления неограниченное время, указать конкретное количество миллисекунд или приказать этой функции проверить состояние порта завершения и немедленно вернуть управление вызвавшей программе.

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

Безусловно, во многих ситуациях вы можете обойтись и без портов завершения ввода/вывода, однако очень часто этот механизм оказывается весьма удобным.

Пример программы

В листинге 2 приведен исходный код простой программы, использующей перекрывающийся ввод/вывод. Эта программа позволяет осуществлять обмен сообщениями между клиентом и сервером. Если вы запустили ее без аргументов командной строки, она начинает работу в режиме сервера. Если в командной строке вы указали имя компьютера, программа переходит в режим клиента и пытается соединиться с сервером, который обладает указанным именем. Если вы хотите соединиться с сервером, работающим на локальном компьютере, укажите в качестве имени компьютера точку.

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