Usando o Assíncrono para Acesso a Arquivos (Visual Basic)

Use o recurso Async para acessar arquivos. Usando o recurso Assíncrono, você pode chamar métodos assíncronos sem usar retornos de chamada ou dividir seu código entre vários métodos ou expressões lambda. Para tornar o código síncrono assíncrono, chame um método assíncrono em vez de um método síncrono e adicione algumas palavras-chave ao código.

Considere adicionar assincronia nas chamadas de acesso a arquivos por estes motivos:

  • 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. Se o thread de interface do usuário precisar executar um código que leve 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 de interface do usuário possa processar novamente a entrada do teclado e do mouse e outros eventos.

  • A assíncrona melhora a escalabilidade de ASP.NET e outros aplicativos baseados em servidor, reduzindo a necessidade de threads. Se o aplicativo usar um tópico dedicado por resposta e mil solicitações estiverem sendo tratadas simultaneamente, serão necessários mil tópicos. As operações assíncronas geralmente não precisam usar um thread durante a espera. Elas podem usar o thread de conclusão de E/S existente rapidamente no final.

  • A latência de uma operação de acesso a arquivos pode ser muito baixa nas condições atuais, mas a latência pode aumentar muito no futuro. Por exemplo, um arquivo pode ser movido para um servidor que está em todo o mundo.

  • A sobrecarga adicional de usar o recurso Async é pequena.

  • Várias operações de E/S assíncronas podem ser executadas sem bloquear o thread de chamada.

Executar os exemplos

Para executar os exemplos neste tópico, crie um aplicativo WPF ou um aplicativo do Windows Forms e, em seguida, adicione um Botão. No evento Click do botão, adicione uma chamada para o primeiro método em cada exemplo.

Nos exemplos a seguir, inclua as declarações a seguir Imports.

Imports System
Imports System.Collections.Generic
Imports System.Diagnostics
Imports System.IO
Imports System.Text
Imports System.Threading.Tasks

Uso da classe FileStream

Os exemplos neste tópico usam a FileStream classe, que tem uma opção que faz com que a E/S assíncrona ocorra no nível do sistema operacional. Usando essa opção, você pode evitar bloquear um thread do ThreadPool em muitos casos. Para habilitar essa opção, especifique o argumento useAsync=true ou options=FileOptions.Asynchronous na chamada do construtor.

Você não pode usar essa opção com StreamReader e StreamWriter se abri-la diretamente especificando um caminho de arquivo. No entanto, você poderá usar essa opção se fornecer um Stream que a classe FileStream abriu. As chamadas assíncronas são mais rápidas em aplicativos de interface do usuário mesmo se um thread do ThreadPool estiver bloqueado, porque o thread da interface do usuário não é bloqueado durante a espera.

Gravar texto

O exemplo a seguir grava texto em um arquivo. A cada instrução await, o método sai imediatamente. Quando o arquivo de E/S for concluído, o método continuará na instrução após a instrução await. O modificador assíncrono está na definição de métodos que usam a instrução await.

Public Async Sub ProcessWrite()
    Dim filePath = "temp2.txt"
    Dim text = "Hello World" & ControlChars.CrLf

    Await WriteTextAsync(filePath, text)
End Sub

Private Async Function WriteTextAsync(filePath As String, text As String) As Task
    Dim encodedText As Byte() = Encoding.Unicode.GetBytes(text)

    Using sourceStream As New FileStream(filePath,
        FileMode.Append, FileAccess.Write, FileShare.None,
        bufferSize:=4096, useAsync:=True)

        Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
    End Using
End Function

O exemplo original tem a instrução Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length), que é uma contração das duas seguintes instruções:

Dim theTask As Task = sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
Await theTask

A primeira instrução retorna uma tarefa e faz com que o processamento de arquivos seja iniciado. A segunda instrução com o await faz com que o método saia imediatamente e retorne uma tarefa diferente. Quando o processamento do arquivo é concluído posteriormente, a execução retorna para a instrução após a await. Para obter mais informações, consulte Fluxo de Controle em Programas Assíncronos (Visual Basic).

Ler texto

O exemplo a seguir lê o texto de um arquivo. O texto é armazenado em buffer e, nesse caso, colocado em um StringBuilder. Diferentemente do exemplo anterior, a avaliação de await produz um valor. O método ReadAsync retorna um Task<Int32>, portanto, a avaliação do await produz um valor Int32 (numRead) após a conclusão da operação. Para obter mais informações, consulte Tipos de retorno assíncronos (Visual Basic).

Public Async Sub ProcessRead()
    Dim filePath = "temp2.txt"

    If File.Exists(filePath) = False Then
        Debug.WriteLine("file not found: " & filePath)
    Else
        Try
            Dim text As String = Await ReadTextAsync(filePath)
            Debug.WriteLine(text)
        Catch ex As Exception
            Debug.WriteLine(ex.Message)
        End Try
    End If
End Sub

Private Async Function ReadTextAsync(filePath As String) As Task(Of String)

    Using sourceStream As New FileStream(filePath,
        FileMode.Open, FileAccess.Read, FileShare.Read,
        bufferSize:=4096, useAsync:=True)

        Dim sb As New StringBuilder

        Dim buffer As Byte() = New Byte(&H1000) {}
        Dim numRead As Integer
        numRead = Await sourceStream.ReadAsync(buffer, 0, buffer.Length)
        While numRead <> 0
            Dim text As String = Encoding.Unicode.GetString(buffer, 0, numRead)
            sb.Append(text)

            numRead = Await sourceStream.ReadAsync(buffer, 0, buffer.Length)
        End While

        Return sb.ToString
    End Using
End Function

Várias operações assíncronas de E/S

O exemplo a seguir inicia várias operações de gravação assíncronas. O runtime enfileira essas operações, e a implementação subjacente pode usar async I/O do sistema operacional (OS) ou threads de pool, dependendo da plataforma e da configuração, portanto, a concorrência efetiva depende do sistema operacional e do hardware. Para cada arquivo, o WriteAsync método retorna uma tarefa que é adicionada a uma lista de tarefas. A Await Task.WhenAll(tasks) instrução sai do método e é retomada dentro do método quando o processamento de arquivos é concluído para todas as tarefas.

O exemplo fecha todas as instâncias de FileStream em um bloco Finally após as tarefas serem concluídas. Se cada FileStream fosse criado em uma instrução Using, o FileStream poderia ser descartado antes da conclusão da tarefa.

A abordagem assíncrona evita bloquear o thread de chamada enquanto a operação de E/S está pendente. Em muitos casos, os aprimoramentos de taxa de transferência dependem do sistema operacional, do hardware e, em algumas plataformas, do comportamento de runtime do .NET, como limites de pool de threads e agendamento.

Public Async Sub ProcessWriteMult()
    Dim folder = "tempfolder\"
    Dim tasks As New List(Of Task)
    Dim sourceStreams As New List(Of FileStream)

    Try
        For index = 1 To 10
            Dim text = "In file " & index.ToString & ControlChars.CrLf

            Dim fileName = "thefile" & index.ToString("00") & ".txt"
            Dim filePath = folder & fileName

            Dim encodedText As Byte() = Encoding.Unicode.GetBytes(text)

            Dim sourceStream As New FileStream(filePath,
                FileMode.Append, FileAccess.Write, FileShare.None,
                bufferSize:=4096, useAsync:=True)

            Dim theTask As Task = sourceStream.WriteAsync(encodedText, 0, encodedText.Length)
            sourceStreams.Add(sourceStream)

            tasks.Add(theTask)
        Next

        Await Task.WhenAll(tasks)
    Finally
        For Each sourceStream As FileStream In sourceStreams
            sourceStream.Close()
        Next
    End Try
End Sub

Ao usar os métodos WriteAsync e ReadAsync, você pode especificar um CancellationToken, para cancelar o fluxo intermediário da operação. Para obter mais informações, consulte Ajuste fino de seu aplicativo assíncrono (Visual Basic) e Cancelamento em threads gerenciados.

Consulte também