Usando o Async para acesso a arquivos (Visual Basic)

Use a funcionalidade Assíncrona para aceder a ficheiros. Usando o recurso Assíncrono, você pode chamar métodos assíncronos sem usar retornos de chamada ou dividir seu código em 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 assíncronia às chamadas de acesso a ficheiros por estas razões:

  • A assincronia torna os aplicativos da interface do usuário mais responsivos porque o thread da interface do usuário que inicia a operação pode executar outro trabalho. Se a thread da interface tiver de executar código que demora muito tempo (por exemplo, mais de 50 milissegundos), a interface pode congelar até que a E/S esteja completa e a thread da interface possa novamente processar entradas de teclado e rato e outros eventos.

  • A assincronia melhora a escalabilidade de ASP.NET e outros aplicativos baseados em servidor, reduzindo a necessidade de threads. Se o aplicativo usa um thread dedicado por resposta e mil solicitações estão sendo tratadas simultaneamente, mil threads são necessários. As operações assíncronas geralmente não precisam usar um thread durante a espera. Eles usam o thread de conclusão de E/S existente brevemente no final.

  • A latência de uma operação de acesso a ficheiros pode ser muito baixa nas condições atuais, mas pode aumentar bastante no futuro. Por exemplo, um ficheiro pode ser movido para um servidor do outro lado do mundo.

  • A sobrecarga adicional de usar o recurso assíncrono é pequena.

  • Múltiplas operações de E/S assíncronas podem ser executadas sem bloquear a thread que fez a chamada.

Executa os exemplos

Para executar os exemplos deste tópico, crie uma Aplicação WPF ou uma Aplicação Windows Forms e depois adicione um Botão. No evento do botão Click, adicione uma chamada ao primeiro método em cada exemplo.

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

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

Utilização 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 ThreadPool em muitos casos. Para ativar esta 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-los diretamente especificando um caminho de arquivo. No entanto, você pode usar essa opção se fornecer a eles um Stream que a FileStream classe abriu. Chamadas assíncronas são mais rápidas nas aplicações UI mesmo que um thread do ThreadPool esteja bloqueado, porque o thread UI não está bloqueado durante a espera.

Escrever texto

O exemplo a seguir grava texto em um arquivo. A cada instrução await, o método é encerrado imediatamente. Quando a E/S do arquivo estiver concluída, o método será retomado na instrução que segue a instrução await. O modificador assíncrono está na definição dos métodos que utilizam 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 afirmação Await sourceStream.WriteAsync(encodedText, 0, encodedText.Length), que é uma contração das duas afirmações a seguir:

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 do arquivo 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 for concluído, a execução retornará à instrução que se segue ao await. Para obter mais informações, consulte Fluxo de controle em programas assíncronos (Visual Basic).

Ler o texto

O exemplo a seguir lê o texto de um arquivo. O texto é armazenado em buffer e, neste caso, colocado em StringBuilder. Ao contrário do exemplo anterior, a avaliação do await produz um valor. 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. 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

Múltiplas operações assíncronas de I/O

O exemplo seguinte inicia múltiplas operações de escrita assíncronas. O tempo de execução coloca estas operações em fila, e a implementação subjacente pode usar threads de I/O ou pool de threads assíncronas do sistema operativo (SO), dependendo da plataforma e configuração, pelo que a concorrência real depende do sistema operativo e hardware. Para cada ficheiro, o WriteAsync método devolve uma tarefa que é adicionada a uma lista de tarefas. A instrução Await Task.WhenAll(tasks) sai do método e retoma a execução dentro do método quando o processamento do ficheiro é concluído para todas as tarefas.

O exemplo fecha todas as instâncias de FileStream num bloco de Finally depois de as tarefas serem concluídas. Se cada um FileStream fosse criado numa Using declaração, FileStream poderia ser eliminado antes de a tarefa ser concluída.

A abordagem assíncrona evita bloquear o thread de chamada enquanto a I/O está pendente. Em muitos casos, as melhorias de rendimento dependem do sistema operativo, do hardware e, em algumas plataformas, do comportamento em tempo de execução 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, pode especificar CancellationToken para cancelar a operação a meio do fluxo. Para obter mais informações, consulte Fine-Tuning A Sua Aplicação Assíncrona (Visual Basic) e Cancelamento em Threads Geridos.

Ver também