Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Используя асинхронную функцию для доступа к файлам, можно вызывать асинхронные методы без использования обратных вызовов или разделения кода по нескольким методам или лямбда-выражениям. Чтобы сделать синхронный код асинхронным, вызовите асинхронный метод вместо синхронного метода и добавьте в код несколько ключевых слов.
Рассмотрите возможность добавления асинхронности в вызовы доступа к файлам по следующим причинам:
- Asynchrony повышает скорость реагирования приложений пользовательского интерфейса, так как поток пользовательского интерфейса, запускающий операцию, может выполнять другую работу. Если поток пользовательского интерфейса должен выполнять код, который занимает много времени (например, более 50 миллисекунд), пользовательский интерфейс может заморозить до завершения ввода-вывода, а поток пользовательского интерфейса может снова обрабатывать ввод клавиатуры и мыши и другие события.
- Асинхронность повышает масштабируемость ASP.NET и других серверных приложений, уменьшая потребность в потоках. Если приложение использует выделенный поток для каждого ответа и тысячи запросов обрабатываются одновременно, требуются тысячи потоков. Асинхронные операции часто не требуют использования потока в период ожидания. Они используют существующий поток завершения операций ввода-вывода ненадолго в конце.
- Задержка операции доступа к файлам может быть очень низкой в текущих условиях, но задержка может значительно увеличиться в будущем. Например, файл может быть перемещен на сервер, который находится во всем мире.
- Дополнительные затраты на использование функции Async невелики.
- Несколько асинхронных операций ввода-вывода могут выполняться без блокировки вызывающего потока.
Используйте соответствующие классы
Простые примеры в этом разделе демонстрируют File.WriteAllTextAsync и File.ReadAllTextAsync. Для точного управления операциями ввода-вывода файлов используйте FileStream класс, который имеет параметр, позволяющий выполнять асинхронные операции ввода-вывода на уровне операционной системы. С помощью этого параметра можно избежать блокировки потока из пула во многих ситуациях. Чтобы включить этот параметр, укажите аргумент useAsync=true или options=FileOptions.Asynchronous в вызове конструктора.
Этот параметр нельзя использовать вместе с StreamReader и StreamWriter, если открываете их напрямую, указывая путь к файлу. Однако этот параметр можно использовать, если предоставить им Stream, который был открыт классом FileStream. Асинхронные вызовы выполняются быстрее в приложениях пользовательского интерфейса, даже если поток из пула рабочих потоков заблокирован, поскольку поток пользовательского интерфейса не блокируется во время ожидания.
Написание текста
В следующих примерах записывается текст в файл. На каждой инструкции await метод немедленно прекращает выполнение. После завершения ввода-вывода файла метод возобновляется в инструкции, следующей за инструкцией await. Асинхронный модификатор находится в определении методов, использующих инструкцию await.
Простой пример
public async Task SimpleWriteAsync()
{
string filePath = "simple.txt";
string text = $"Hello World";
await File.WriteAllTextAsync(filePath, text);
}
Пример конечного элемента управления
public async Task ProcessWriteAsync()
{
string filePath = "temp.txt";
string text = $"Hello World{Environment.NewLine}";
await WriteTextAsync(filePath, text);
}
async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
В исходном примере имеется выражение await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, которое является сокращением следующих двух операторов:
Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;
Первая инструкция возвращает задачу и приводит к началу обработки файлов. Вторая инструкция с ожиданием приводит к немедленному выходу метода и возврату другой задачи. После завершения обработки файлов выполнение возвращается в инструкцию, которая следует за ожиданием.
Чтение текста
В следующих примерах считывается текст из файла.
Простой пример
public async Task SimpleReadAsync()
{
string filePath = "simple.txt";
string text = await File.ReadAllTextAsync(filePath);
Console.WriteLine(text);
}
Пример конечного элемента управления
Текст буферичен и, в данном случае, помещается в объект StringBuilder. В отличие от предыдущего примера, оценка await создает значение. Метод ReadAsync возвращает значениеTask<Int32>, поэтому оценка await создает Int32 значение numRead после завершения операции. Дополнительные сведения см. в разделе Async Return Types (C#).
public async Task ProcessReadAsync()
{
try
{
string filePath = "temp.txt";
if (File.Exists(filePath) != false)
{
string text = await ReadTextAsync(filePath);
Console.WriteLine(text);
}
else
{
Console.WriteLine($"file not found: {filePath}");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
async Task<string> ReadTextAsync(string filePath)
{
using var sourceStream =
new FileStream(
filePath,
FileMode.Open, FileAccess.Read, FileShare.Read,
bufferSize: 4096, useAsync: true);
var sb = new StringBuilder();
byte[] buffer = new byte[0x1000];
int numRead;
while ((numRead = await sourceStream.ReadAsync(buffer, 0, buffer.Length)) != 0)
{
string text = Encoding.Unicode.GetString(buffer, 0, numRead);
sb.Append(text);
}
return sb.ToString();
}
Несколько асинхронных операций ввода-вывода
В следующих примерах запускается несколько асинхронных операций записи. Среда выполнения ставит в очередь эти операции, и базовая реализация может использовать асинхронный ввод-вывод операционной системы (ОС) или потоки пула потоков в зависимости от платформы и конфигурации, поэтому фактическая конкуренция зависит от ОС и оборудования.
Простой пример
public async Task SimpleParallelWriteAsync()
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 11; index <= 20; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
writeTaskList.Add(File.WriteAllTextAsync(filePath, text));
}
await Task.WhenAll(writeTaskList);
}
Пример конечного элемента управления
Для каждого файла WriteAsync метод возвращает задачу, добавленную в список задач. Оператор await Task.WhenAll(tasks); завершает метод и возобновляет работу в методе при завершении обработки файлов для всех задач.
Пример закрывает все FileStream экземпляры в блоке finally после завершения задач. Если FileStream была вместо этого создана в инструкции using, FileStream может быть удален до завершения задачи.
Асинхронный подход позволяет избежать блокировки вызывающего потока во время ожидания ввода-вывода. Во многих случаях улучшения пропускной способности зависят от ОС, оборудования и на некоторых платформах, поведения среды выполнения .NET, таких как ограничения пула потоков и планирование.
public async Task ProcessMultipleWritesAsync()
{
IList<FileStream> sourceStreams = new List<FileStream>();
try
{
string folder = Directory.CreateDirectory("tempfolder").Name;
IList<Task> writeTaskList = new List<Task>();
for (int index = 1; index <= 10; ++ index)
{
string fileName = $"file-{index:00}.txt";
string filePath = $"{folder}/{fileName}";
string text = $"In file {index}{Environment.NewLine}";
byte[] encodedText = Encoding.Unicode.GetBytes(text);
var sourceStream =
new FileStream(
filePath,
FileMode.Create, FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true);
Task writeTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
sourceStreams.Add(sourceStream);
writeTaskList.Add(writeTask);
}
await Task.WhenAll(writeTaskList);
}
finally
{
foreach (FileStream sourceStream in sourceStreams)
{
sourceStream.Close();
}
}
}
При использовании методов WriteAsync и ReadAsync можно указать CancellationToken, чтобы отменить операцию в середине потока. Дополнительные сведения см. в разделе "Отмена" в управляемых потоках.