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.