Acesso a arquivos assíncronos (C#)Asynchronous file access (C#)

Você pode usar o recurso async para acessar arquivos.You can use the async feature to access files. Usando o recurso async, você pode chamar os métodos assíncronos sem usar retornos de chamada ou dividir seu código em vários métodos ou expressões lambda.By using the async feature, you can call into asynchronous methods without using callbacks or splitting your code across multiple methods or lambda expressions. Para tornar síncrono um código assíncrono, basta chamar um método assíncrono em vez de um método síncrono e adicionar algumas palavras-chave ao código.To make synchronous code asynchronous, you just call an asynchronous method instead of a synchronous method and add a few keywords to the code.

Você pode considerar seguintes motivos para adicionar a assincronia às chamadas de acesso ao arquivo:You might consider the following reasons for adding asynchrony to file access calls:

  • A assincronia torna os aplicativos de interface do usuário mais responsivos porque o thread de interface do usuário que inicia a operação pode executar outro trabalho.Asynchrony makes UI applications more responsive because the UI thread that launches the operation can perform other work. Se o thread de interface do usuário precisar executar o código que leva muito tempo (por exemplo, mais de 50 milissegundos), a interface do usuário poderá congelar até que a E/S seja concluída e o thread da interface do usuário possa processar entradas do mouse e do teclado e outros eventos.If the UI thread must execute code that takes a long time (for example, more than 50 milliseconds), the UI may freeze until the I/O is complete and the UI thread can again process keyboard and mouse input and other events.
  • A assincronia melhora a escalabilidade do ASP.NET e outros aplicativos baseados em servidor reduzindo a necessidade de threads.Asynchrony improves the scalability of ASP.NET and other server-based applications by reducing the need for threads. Se o aplicativo usar um thread dedicado por resposta e mil solicitações forem tratadas simultaneamente, serão necessários mil threads.If the application uses a dedicated thread per response and a thousand requests are being handled simultaneously, a thousand threads are needed. As operações assíncronas geralmente não precisam usar um thread durante a espera.Asynchronous operations often don't need to use a thread during the wait. Elas podem usar o thread de conclusão de E/S existente rapidamente no final.They use the existing I/O completion thread briefly at the end.
  • A latência de uma operação de acesso de arquivo pode ser muito baixa nas condições atuais, mas a latência pode aumentar consideravelmente no futuro.The latency of a file access operation might be very low under current conditions, but the latency may greatly increase in the future. Por exemplo, um arquivo pode ser movido para um servidor que está do outro lado do mundo.For example, a file may be moved to a server that's across the world.
  • A sobrecarga adicional de usar o recurso async é pequena.The added overhead of using the Async feature is small.
  • As tarefas assíncronas podem facilmente ser executadas em paralelo.Asynchronous tasks can easily be run in parallel.

Usar classes apropriadasUse appropriate classes

Os exemplos simples neste tópico demonstram File.WriteAllTextAsync e File.ReadAllTextAsync .The simple examples in this topic demonstrate File.WriteAllTextAsync and File.ReadAllTextAsync. Para controle finito sobre as operações de e/s de arquivo, use a FileStream classe, que tem uma opção que faz com que a e/s assíncrona ocorra no nível do sistema operacional.For finite control over the file I/O operations, use the FileStream class, which has an option that causes asynchronous I/O to occur at the operating system level. Usando essa opção, você pode evitar o bloqueio de um thread de pool de threads em muitos casos.By using this option, you can avoid blocking a thread pool thread in many cases. Para habilitar essa opção, você deve especificar o argumento useAsync=true ou options=FileOptions.Asynchronous na chamada do construtor.To enable this option, you specify the useAsync=true or options=FileOptions.Asynchronous argument in the constructor call.

Você não pode usar essa opção com StreamReader e StreamWriter se as abrir diretamente, especificando um caminho de arquivo.You can't use this option with StreamReader and StreamWriter if you open them directly by specifying a file path. No entanto, você poderá usar essa opção se fornecer um Stream que a classe FileStream abriu.However, you can use this option if you provide them a Stream that the FileStream class opened. As chamadas assíncronas são mais rápidas em aplicativos de interface do usuário, mesmo que um thread do pool de threads seja bloqueado, pois o thread da interface do usuário não é bloqueado duranteAsynchronous calls are faster in UI apps even if a thread pool thread is blocked, because the UI thread isn't blocked during the wait.

Gravar textoWrite text

Os exemplos a seguir gravam o texto em um arquivo.The following examples write text to a file. A cada instrução await, o método sai imediatamente.At each await statement, the method immediately exits. Quando o arquivo de E/S for concluído, o método continuará na instrução após a instrução await.When the file I/O is complete, the method resumes at the statement that follows the await statement. O modificador assíncrono está na definição de métodos que usam a instrução Await.The async modifier is in the definition of methods that use the await statement.

Exemplo simplesSimple example

public async Task SimpleWriteAsync()
{
    string filePath = "simple.txt";
    string text = $"Hello World";

    await File.WriteAllTextAsync(filePath, text);
}

Exemplo de controle finitoFinite control example

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);
}

O exemplo original tem a instrução await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, que é uma contração das duas instruções a seguir:The original example has the statement await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);, which is a contraction of the following two statements:

Task theTask = sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
await theTask;

A primeira instrução retorna uma tarefa e faz com que o processamento do arquivo seja iniciado.The first statement returns a task and causes file processing to start. A segunda instrução com o await faz com que o método saia imediatamente e retorne uma tarefa diferente.The second statement with the await causes the method to immediately exit and return a different task. Quando o processamento do arquivo é concluído posteriormente, a execução retorna para a instrução após a await.When the file processing later completes, execution returns to the statement that follows the await.

Ler textoRead text

Os exemplos a seguir lêem o texto de um arquivo.The following examples read text from a file.

Exemplo simplesSimple example

public async Task SimpleReadAsync()
{
    string filePath = "simple.txt";
    string text = await File.ReadAllTextAsync(filePath);

    Console.WriteLine(text);
}

Exemplo de controle finitoFinite control example

O texto é armazenado em buffer e, nesse caso, colocado em um StringBuilder.The text is buffered and, in this case, placed into a StringBuilder. Diferentemente do exemplo anterior, a avaliação de await produz um valor.Unlike in the previous example, the evaluation of the await produces a value. O ReadAsync método retorna um Task <Int32>, de modo que a avaliação do Await produz um Int32 valor numRead após a conclusão da operação.The ReadAsync method returns a Task<Int32>, so the evaluation of the await produces an Int32 value numRead after the operation completes. Para obter mais informações, consulte Tipos de retorno assíncronos (C#).For more information, see 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();
}

E/s assíncrona paralelaParallel asynchronous I/O

Os exemplos a seguir demonstram o processamento paralelo com a gravação de 10 arquivos de texto.The following examples demonstrate parallel processing by writing 10 text files.

Exemplo simplesSimple example

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);
}

Exemplo de controle finitoFinite control example

Para cada arquivo, o método WriteAsync retorna uma tarefa que é então adicionada a uma lista de tarefas.For each file, the WriteAsync method returns a task that is then added to a list of tasks. A instrução await Task.WhenAll(tasks); sai do método e retoma no método quando o processamento do arquivo é concluído para todas as tarefas.The await Task.WhenAll(tasks); statement exits the method and resumes within the method when file processing is complete for all of the tasks.

O exemplo fecha todas as instâncias de FileStream em um bloco finally após as tarefas serem concluídas.The example closes all FileStream instances in a finally block after the tasks are complete. Se cada FileStream foi criado em uma instrução using, o FileStream pode ter sido descartado antes de a tarefa ter sido concluída.If each FileStream was instead created in a using statement, the FileStream might be disposed of before the task was complete.

Qualquer aumento de desempenho é quase totalmente o processamento paralelo e não o processamento assíncrono.Any performance boost is almost entirely from the parallel processing and not the asynchronous processing. As vantagens da assincronia são que ele não liga vários threads e que ele não vincula o thread da interface do usuário.The advantages of asynchrony are that it doesn't tie up multiple threads, and that it doesn't tie up the user interface thread.

public async Task ProcessMulitpleWritesAsync()
{
    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();
        }
    }
}

Ao usar os métodos WriteAsync e ReadAsync, você pode especificar um CancellationToken, que pode ser usado para cancelar o fluxo intermediário da operação.When using the WriteAsync and ReadAsync methods, you can specify a CancellationToken, which you can use to cancel the operation mid-stream. Para saber mais, confira Cancelamento em threads gerenciados.For more information, see Cancellation in managed threads.

Confira tambémSee also