Поделиться через


Синхронный и перекрываемый канал ввода-вывода

Функции ReadFile, WriteFile, TransactNamedPipe и ConnectNamedPipe могут выполнять входные и выходные операции с каналом синхронно или асинхронно. Когда функция выполняется синхронно, она не возвращается до завершения выполняемой ею операции. Это означает, что выполнение вызывающего потока может быть заблокировано на неопределенный срок, пока он ожидает завершения отнимающей много времени операции. Когда функция выполняется асинхронно, она возвращается немедленно, даже если операция не была завершена. Это позволяет выполнять трудоемкую операцию в фоновом режиме, в то время как вызывающий поток свободен для выполнения других задач.

Использование асинхронного ввода-вывода позволяет серверу конвейера использовать цикл, выполняющий следующие действия:

  1. Укажите несколько объектов событий в вызове функции wait и дождитесь, пока один из объектов будет установлен в состояние сигнала.
  2. Используйте возвращаемое значение функции ожидания, чтобы определить, какая перекрывающаяся операция завершена.
  3. Выполните задачи, необходимые для очистки завершенной операции и запуска следующей операции для этого дескриптора канала. Это может включать запуск другой перекрывающейся операции для того же дескриптора канала.

Перекрывающиеся операции позволяют одному каналу одновременно считывать и записывать данные, а для одного потока выполнять одновременные операции ввода-вывода с несколькими дескрипторами канала. Это позволяет серверу конвейера с одним потоком эффективно обрабатывать обмен данными с несколькими клиентами канала. Пример см. в разделе Сервер именованного канала с использованием перекрывающихся операций ввода-вывода.

Чтобы сервер конвейера использовал синхронные операции для взаимодействия с несколькими клиентами, он должен создать отдельный поток для каждого клиента канала, чтобы один или несколько потоков могли выполняться в ожидании других потоков. Пример многопоточного сервера канала, использующего синхронные операции, см. в разделе Сервер многопоточных каналов.

Включение асинхронной операции

Функции ReadFile, WriteFile, TransactNamedPipe и ConnectNamedPipe можно выполнять асинхронно, только если для указанного дескриптора канала включен режим перекрытия и указан допустимый указатель на структуру OVERLAPPED . Если указатель OVERLAPPED имеет значение NULL, возвращаемое значение функции может неправильно указывать на то, что операция завершена. Поэтому настоятельно рекомендуется при создании дескриптора с FILE_FLAG_OVERLAPPED и асинхронного поведения всегда указывать допустимую структуру OVERLAPPED .

Элемент hEvent указанной структуры OVERLAPPED должен содержать дескриптор объекта события сброса вручную. Это объект синхронизации, созданный функцией CreateEvent . Поток, инициирующий перекрывающуюся операцию, использует объект события для определения завершения операции. Не следует использовать дескриптор канала для синхронизации при выполнении одновременных операций с тем же дескриптором, так как невозможно узнать, какая операция привела к сигналу дескриптора канала. Единственный надежный способ выполнения одновременных операций с тем же дескриптором канала — использовать отдельную структуру OVERLAPPED с собственным объектом события для каждой операции. Дополнительные сведения об объектах событий см. в разделе Синхронизация.

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

Когда операции ReadFile, WriteFile, TransactNamedPipe и ConnectNamedPipe выполняются асинхронно, происходит одно из следующих действий:

  • Если операция завершена при возврате функции, возвращаемое значение указывает на успешное или неудачное выполнение операции. Если возникает ошибка, возвращаемое значение равно нулю, а функция GetLastError возвращает нечто, отличное от ERROR_IO_PENDING.
  • Если операция не была завершена после возврата функции, возвращаемое значение равно нулю, а GetLastError возвращает ERROR_IO_PENDING. В этом случае вызывающий поток должен дождаться завершения операции. Затем вызывающий поток должен вызвать функцию GetOverlappedResult , чтобы определить результаты.

Использование подпрограмм завершения

Функции ReadFileEx и WriteFileEx предоставляют другую форму перекрывающихся операций ввода-вывода. В отличие от перекрывающихся функций ReadFile и WriteFile , которые используют объект события для сигнала о завершении, расширенные функции задают подпрограмму завершения. Подпрограмма завершения — это функция, которая помещается в очередь для выполнения после завершения операции чтения или записи. Подпрограмма завершения не выполняется до тех пор, пока поток с именами ReadFileEx и WriteFileEx не запустит оповещенную операцию ожидания путем вызова одной из функций ожидания с оповещением с параметром fAlertable , установленным в значение TRUE. В операции ожидания с оповещением функции также возвращаются, когда подпрограмма завершения ReadFileEx или WriteFileEx помещается в очередь для выполнения. Сервер канала может использовать расширенные функции для выполнения последовательности операций чтения и записи для каждого клиента, подключающегося к нему. Каждая операция чтения или записи в последовательности задает подпрограмму завершения, а каждая подпрограмма завершения инициирует следующий шаг последовательности. Пример см. в разделе Сервер именованных каналов, использующий подпрограммы завершения.