Синхронные и асинхронные операции ввода-вывода

См. также примеры приложений, связанных с вводом-выводом.

Существует два типа синхронизации ввода-вывода: синхронный ввод-вывод и асинхронный ввод-вывод. Асинхронный ввод-вывод также называется перекрывающимся вводом-выводом.

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

На следующем рисунке показаны два типа синхронизации.

синхронный и асинхронный ввод-вывод

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

Рекомендации по синхронному и асинхронному вводу-выводу

Если файл или устройство открыты для синхронного ввода-вывода (то есть FILE_FLAG_OVERLAPPED не указан), последующие вызовы таких функций, как WriteFile , могут блокировать выполнение вызывающего потока до тех пор, пока не произойдет одно из следующих событий:

  • Операция ввода-вывода завершается (в этом примере — запись данных).
  • Ошибка ввода-вывода. (Например, канал закрывается с другого конца.)
  • Ошибка в самом вызове (например, один или несколько параметров недопустимы).
  • Другой поток в процессе вызывает функцию CancelSynchronousIo , используя дескриптор потока блокировки, который завершает операции ввода-вывода для этого потока, завершая операцию ввода-вывода.
  • Заблокированный поток завершается системой; Например, сам процесс завершается или другой поток вызывает функцию TerminateThread с помощью дескриптора заблокированного потока. (Это, как правило, считается последним и не очень хорошим дизайном приложения.)

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

Процесс открывает файл для асинхронного ввода-вывода в вызове CreateFile , указав флаг FILE_FLAG_OVERLAPPED в параметре dwFlagsAndAttributes . Если FILE_FLAG_OVERLAPPED не указан, файл открывается для синхронного ввода-вывода. При открытии файла для асинхронного ввода-вывода в вызов ReadFile и WriteFile передается указатель на структуру OVERLAPPED. При выполнении синхронного ввода-вывода эта структура не требуется в вызовах ReadFile и WriteFile.

Примечание

Если файл или устройство открывается для асинхронного ввода-вывода, последующие вызовы функций, таких как WriteFile с использованием этого дескриптора, обычно возвращаются немедленно, но также могут работать синхронно по отношению к заблокированному выполнению. Для получения дополнительной информации см. https://support.microsoft.com/kb/156932.

 

Хотя CreateFile является наиболее распространенной функцией, используемой для открытия файлов, томов дисков, анонимных каналов и других подобных устройств, операции ввода-вывода также можно выполнять с помощью дескриптора типов из других системных объектов, таких как сокет, созданный сокетом , или функции accept .

Дескрипторы объектов каталога получаются путем вызова функции CreateFile с атрибутом FILE_FLAG_BACKUP_SEMANTICS . Дескрипторы каталогов почти никогда не используются. Приложения резервного копирования являются одним из немногих приложений, которые обычно используют их.

После открытия объекта файла для асинхронного ввода-вывода структура OVERLAPPED должна быть правильно создана, инициализирована и передана в каждом вызове таких функций, как ReadFile и WriteFile. При использовании структуры OVERLAPPED в асинхронных операциях чтения и записи учитывайте следующее:

  • Не отменяйте выделение и не изменяйте структуру OVERLAPPED или буфер данных, пока не будут завершены все асинхронные операции ввода-вывода в объекте файла.
  • Если вы объявляете указатель на структуру OVERLAPPED как локальную переменную, не закрывайте локальную функцию, пока не будут завершены все асинхронные операции ввода-вывода для объекта файла. Если локальная функция завершается преждевременно, структура OVERLAPPED выйдет из область и будет недоступна для всех функций ReadFile или WriteFile, с которыми она сталкивается за пределами этой функции.

Можно также создать событие и поместить дескриптор в структуру OVERLAPPED ; Затем функции ожидания можно использовать для ожидания завершения операции ввода-вывода путем ожидания дескриптора события.

Как было сказано ранее, при работе с асинхронным дескриптором приложения должны проявлять осторожность при определении того, когда следует освобождать ресурсы, связанные с указанной операцией ввода-вывода для этого дескриптора. Если дескриптор освобождается преждевременно, ReadFile или WriteFile могут неправильно сообщить о завершении операции ввода-вывода. Кроме того, функция WriteFile иногда возвращает значение TRUE со значением GetLastErrorERROR_SUCCESS, даже если использует асинхронный дескриптор (который также может возвращать ЗНАЧЕНИЕ FALSE с ERROR_IO_PENDING). Программисты, привыкшие к синхронному проектированию операций ввода-вывода, обычно освобождают ресурсы буфера данных на этом этапе, так как true и ERROR_SUCCESS означают, что операция завершена. Однако если порты завершения ввода-вывода используются с этим асинхронным дескриптором, пакет завершения также будет отправлен, даже если операция ввода-вывода завершена немедленно. Другими словами, если приложение освобождает ресурсы после того, как WriteFile возвращает значение TRUE с ERROR_SUCCESS в дополнение к подпрограмме порта завершения ввода-вывода, оно будет иметь двойное условие ошибки. В этом примере рекомендуется разрешить подпрограмме порта завершения нести полную ответственность за все операции освобождения таких ресурсов.

Система не поддерживает указатель на файл на асинхронных дескрипторах файлов и устройств, поддерживающих указатели на файлы (то есть устройства поиска), поэтому положение файла должно передаваться функциям чтения и записи в связанных членах данных смещения структуры OVERLAPPED . Дополнительные сведения см. в разделах WriteFile и ReadFile.

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

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

Чтобы отменить все ожидающие асинхронные операции ввода-вывода, используйте один из следующих способов:

  • CancelIo — эта функция отменяет только операции, выданные вызывающим потоком для указанного дескриптора файла.
  • CancelIoEx — эта функция отменяет все операции, выполняемые потоками для указанного дескриптора файла.

Используйте CancelSynchronousIo для отмены ожидающих синхронных операций ввода-вывода.

Функции ReadFileEx и WriteFileEx позволяют приложению указать подпрограмму для выполнения (см. FileIOCompletionRoutine) при выполнении асинхронного запроса ввода-вывода.