Async/await и открытие FileStream?

Я столкнулся с следующим вопросом, пытаясь определить, правильно ли я использовал методы Stream, такие как ReadAsync и CopyToAsync: Сценарий производительности чтения файлов С# 4.5 vs async

В этом вопросе я прочитал следующее в принятом ответе:

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

В своем асинхронном IO-коде он использовал следующее, чтобы открыть FileStream 'асинхронно':

var file = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true)

Итак, мне было интересно, собираетесь ли вы использовать такие методы, как CopyToAsync, следует ли открывать базовый FileStream, как показано выше?, а не делать что-то простое, например следующее:

File.Open(filename, FileMode.Open)

Вот как демонстрируется пример в фактической документации для CopyToAsync для открытия базового FileStream: https://msdn.microsoft.com/en-us/library/hh159084(v=vs.110).aspx

Если неважно, каким образом открывается базовый FileStream, что делает параметр useAsync конструктора FileStream?

Ответ 1

Итак, мне было интересно, собираетесь ли вы использовать такие методы, как CopyToAsync, следует ли открывать базовый FileStream, как показано выше?

Да. Причина в основном историческая.

Во-первых, в Windows HANDLE (включая дескрипторы файлов) должно быть открыто/создано явно с асинхронным флагом, если вы хотите сделать асинхронный (OVERLAPPED) на них.

Однако старая строка Windows 95/98/ME поддерживает только асинхронные операции с последовательным портом и IOCTL (драйвер устройства). Асинхронный ввод-вывод на дисковых файлах не поддерживался на этой платформе. И оригинальный .NET поддерживал 98/ME, поэтому оригинальный FileStream использовал только синхронный ввод-вывод. Я думаю (но не совсем уверен), что методы APM (например, FileStream.BeginRead) на Win98/ME, вероятно, были просто реализованы с использованием так называемого "называемый " асинхронные делегаты " (которые просто выполняют синхронный метод, например FileStream.Read в потоке пула потоков).

Итак, историческая причина, по которой дескрипторы потока файлов не были открыты с асинхронным флагом по умолчанию.

Вот как демонстрирует пример в фактической документации для CopyToAsync

К сожалению, многие примеры MSDN довольно низкого качества. Они в порядке, если вы подходите к ним с точки зрения "здесь пример того, как назвать этот конкретный метод", но не настолько велики с точки зрения "здесь пример кода производственного качества, который использует этот метод".

Ответ 2

Итак, мне было интересно, собираетесь ли вы использовать такие методы, как CopyToAsync, следует ли открывать базовый FileStream, как показано выше, а не делать что-то простое, например File.Open?

Я использовал ILSpy для декомпиляции и посмотрел File.Open.

public static FileStream Open(string path, FileMode mode)
{
    return File.Open(path, 
                     mode, 
                     (mode == FileMode.Append) 
                         ? FileAccess.Write 
                         : FileAccess.ReadWrite, 
                     FileShare.None);
}

Что называется:

public static FileStream Open(string path, FileMode mode, FileAccess access, FileShare share)
{
    return new FileStream(path, mode, access, share);
}

И этот конкретный конструктор FileStream проходит в false для параметра useAsync. Итак, да, похоже, это имеет значение. Однако вы все равно можете использовать API async, и он все равно будет работать так, как вы ожидали.

Как говорит Ханс Пассант:

В базовом вызове CreateFile() используется опция FILE_FLAG_OVERLAPPED. Это позволяет перекрывать I/O, механизм, который позволяет асинхронные чтения и записи на уровне winapi.

Класс FileStream имеет _isAsync bool, и это означает: "Если async IO не поддерживается на этой платформе или если этот FileStream не был открыт с помощью FileOptions.Asynchronous.".

Опять же, вы все равно получаете Task, который представляет собой асинхронную операцию по вашему желанию.

Ответ 3

Веб-сайт MSDN говорит:

useAsync

Тип: System.Boolean

Указывает, следует ли использовать асинхронный ввод-вывод или синхронный ввод-вывод. Однако обратите внимание, что базовая операционная система может не поддерживать асинхронный ввод-вывод, поэтому при указании true ручка может быть открыта синхронно в зависимости от платформы. Когда асинхронно открывается, BeginRead и BeginWrite работают лучше на больших читает или пишет, но они могут быть намного медленнее для небольших чтений или пишет. Если приложение предназначено для использования асинхронный ввод/вывод, установите для параметра useAsync значение true. С помощью асинхронный ввод-вывод правильно может ускорить работу приложений, коэффициент 10, но с его использованием без перепроектирования приложения для асинхронный ввод-вывод может снизить производительность на столько, сколько 10.