Partilhar via


Programação assíncrona com Async e Await (Visual Basic)

Você pode evitar gargalos de desempenho e melhorar a capacidade de resposta geral do seu aplicativo usando programação assíncrona. No entanto, as técnicas tradicionais para escrever aplicações assíncronas podem ser complicadas, dificultando a escrita, depuração e manutenção.

O Visual Studio 2012 introduziu uma abordagem simplificada, a programação assíncrona, que aproveita o suporte assíncrono no .NET Framework 4.5 e superior, bem como no Tempo de Execução do Windows. O compilador faz o trabalho difícil que o desenvolvedor costumava fazer, e seu aplicativo mantém uma estrutura lógica que se assemelha ao código síncrono. Como resultado, você obtém todas as vantagens da programação assíncrona com uma fração do esforço.

Este tópico fornece uma visão geral de quando e como usar a programação assíncrona e inclui links para tópicos de suporte que contêm detalhes e exemplos.

O processamento assíncrono melhora a capacidade de resposta

A assincronia é essencial para atividades que estão potencialmente bloqueando, como quando seu aplicativo acessa a Web. O acesso a um recurso da Web às vezes é lento ou atrasado. Se tal atividade for bloqueada dentro de um processo síncrono, todo o aplicativo deverá aguardar. Em um processo assíncrono, o aplicativo pode continuar com outro trabalho que não depende do recurso da Web até que a tarefa potencialmente bloqueada seja concluída.

A tabela a seguir mostra áreas típicas onde a programação assíncrona melhora a capacidade de resposta. As APIs listadas do .NET Framework 4.5 e do Tempo de Execução do Windows contêm métodos que oferecem suporte à programação assíncrona.

Área de aplicação Apoiar APIs que contêm métodos assíncronos
Acesso à Web HttpClient, SyndicationClient
Trabalhar com ficheiros StorageFile, StreamWriter, StreamReader, XmlReader
Trabalhar com imagens MediaCapture, BitmapEncoder, BitmapDecoder
Programação WCF Operações Síncronas e Assíncronas

A assincronia se mostra especialmente valiosa para aplicativos que acessam o thread da interface do usuário, porque todas as atividades relacionadas à interface do usuário geralmente compartilham um thread. Se algum processo for bloqueado em um aplicativo síncrono, todos serão bloqueados. Seu aplicativo para de responder e você pode concluir que ele falhou quando, em vez disso, está apenas esperando.

Quando você usa métodos assíncronos, o aplicativo continua a responder à interface do usuário. Você pode redimensionar ou minimizar uma janela, por exemplo, ou pode fechar o aplicativo se não quiser esperar que ele termine.

A abordagem baseada em assincronia adiciona o equivalente a uma transmissão automática à lista de opções que você pode escolher ao projetar operações assíncronas. Ou seja, você obtém todos os benefícios da programação assíncrona tradicional, mas com muito menos esforço do desenvolvedor.

Os métodos assíncronos são mais fáceis de escrever

As palavras-chave Async e Await no Visual Basic são o coração da programação assíncrona. Usando essas duas palavras-chave, você pode usar recursos no .NET Framework ou no Tempo de Execução do Windows para criar um método assíncrono quase tão facilmente quanto criar um método síncrono. Métodos assíncronos que você define usando Async e Await são chamados de métodos assíncronos.

O exemplo a seguir mostra um método assíncrono. Quase tudo no código deve parecer completamente familiar para você. Os comentários chamam a atenção para os recursos que você adiciona para criar a assincronia.

Você pode encontrar um arquivo de exemplo completo do Windows Presentation Foundation (WPF) no final deste tópico e pode baixar o exemplo de Async Sample: Example de "Asynchronous Programming with Async and Await".

' Three things to note about writing an Async Function:
'  - The function has an Async modifier.
'  - Its return type is Task or Task(Of T). (See "Return Types" section.)
'  - As a matter of convention, its name ends in "Async".
Async Function AccessTheWebAsync() As Task(Of Integer)
    Using client As New HttpClient()
        ' Call and await separately.
        '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
        '  - getStringTask stores the task we get from the call to GetStringAsync.
        '  - Task(Of String) means it is a task which returns a String when it is done.
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com/dotnet")
        ' You can do other work here that doesn't rely on the string from GetStringAsync.
        DoIndependentWork()
        ' The Await operator suspends AccessTheWebAsync.
        '  - AccessTheWebAsync does not continue until getStringTask is complete.
        '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
        '  - Control resumes here when getStringTask is complete.
        '  - The Await operator then retrieves the String result from getStringTask.
        Dim urlContents As String = Await getStringTask
        ' The Return statement specifies an Integer result.
        ' A method which awaits AccessTheWebAsync receives the Length value.
        Return urlContents.Length

    End Using

End Function

Se AccessTheWebAsync não tiver nenhum trabalho que possa fazer entre chamar GetStringAsync e aguardar a sua conclusão, podes simplificar o teu código chamando e aguardando numa única instrução.

Dim urlContents As String = Await client.GetStringAsync()

As características a seguir resumem o que torna o exemplo anterior um método assíncrono:

  • A assinatura do método inclui o modificador Async.

  • O nome de um método assíncrono, por convenção, termina com um sufixo "Async".

  • O tipo de retorno é um dos seguintes tipos:

    • Task(Of TResult) se o seu método tiver uma instrução return na qual o operando tenha o tipo TResult.
    • Task se o seu método não tem nenhuma instrução return ou tem uma instrução return sem operando.
    • Sub se você estiver escrevendo um manipulador de eventos assíncrono.

    Para obter mais informações, consulte "Tipos e parâmetros de retorno" mais adiante neste tópico.

  • O método geralmente inclui pelo menos uma expressão await, que marca um ponto em que o método não pode continuar até que a operação assíncrona esperada seja concluída. Enquanto isso, o método é suspenso e o controle retorna ao chamador do método. A próxima seção deste tópico ilustra o que acontece no ponto de suspensão.

Em métodos assíncronos, você usa as palavras-chave e os tipos fornecidos para indicar o que deseja fazer, e o compilador faz o resto, incluindo acompanhar o que deve acontecer quando o controle retorna a um ponto de espera em um método suspenso. Alguns processos de rotina, como loops e tratamento de exceções, podem ser difíceis de lidar no código assíncrono tradicional. Em um método assíncrono, você escreve esses elementos da mesma forma que faria em uma solução síncrona e o problema é resolvido.

Para obter mais informações sobre assincronia em versões anteriores do .NET Framework, consulte TPL e programação assíncrona tradicional do .NET Framework.

O que acontece em um método Assíncrono

A coisa mais importante a entender na programação assíncrona é como o fluxo de controle se move de método para método. O diagrama a seguir conduz você através do processo:

Diagrama que mostra o rastreamento de um programa assíncrono.

Os números no diagrama correspondem aos seguintes passos:

  1. Um manipulador de eventos chama e aguarda o método assíncrono AccessTheWebAsync .

  2. AccessTheWebAsync cria uma instância HttpClient e chama o método assíncrono GetStringAsync para baixar o conteúdo de um site como uma cadeia de caracteres.

  3. Algo acontece em GetStringAsync que suspende o seu progresso. Talvez deva esperar por um site para baixar ou alguma outra atividade de bloqueio. Para evitar o bloqueio de recursos, GetStringAsync cede o controle ao chamador, AccessTheWebAsync.

    GetStringAsync retorna um Task(Of TResult) onde TResult é uma cadeia de caracteres e AccessTheWebAsync atribui a tarefa à getStringTask variável. A tarefa representa o processo contínuo da chamada para GetStringAsync, com o compromisso de produzir um valor de string real quando o trabalho for concluído.

  4. Como getStringTask ainda não foi esperado, AccessTheWebAsync pode continuar com outras tarefas que não dependem do resultado final de GetStringAsync. Esse trabalho é representado por uma chamada para o método síncrono DoIndependentWork.

  5. DoIndependentWork é um método síncrono que faz seu trabalho e retorna ao seu chamador.

  6. AccessTheWebAsync ficou sem trabalho que possa realizar sem obter um resultado de getStringTask. AccessTheWebAsync próximo deseja calcular e retornar o comprimento da cadeia de caracteres baixada, mas o método não pode calcular esse valor até que o método tenha a cadeia de caracteres.

    Portanto, AccessTheWebAsync usa um operador await para suspender seu progresso e entregar o controle ao método chamado AccessTheWebAsync. AccessTheWebAsync retorna um Task(Of Integer) para quem chamou. A tarefa representa uma promessa de produzir um resultado inteiro que é o comprimento da cadeia de caracteres baixada.

    Observação

    Se GetStringAsync (e, portanto, getStringTask) estiver completo antes de AccessTheWebAsync aguardar por ele, o controle permanece em AccessTheWebAsync. A despesa de suspender e depois retornar a AccessTheWebAsync seria desperdiçada se o chamado processo assíncrono (getStringTask) já tiver sido concluído e AccessTheWebSync não tiver que esperar pelo resultado final.

    Dentro do chamador (o manipulador de eventos neste exemplo), o padrão de processamento continua. O chamador pode fazer outro trabalho que não depende do resultado do AccessTheWebAsync antes de aguardar esse resultado, ou o chamador pode aguardar imediatamente. O manipulador de eventos está aguardando AccessTheWebAsync, e AccessTheWebAsync está aguardando GetStringAsync.

  7. GetStringAsync completa e produz um resultado de cadeia de caracteres. O resultado da cadeia de caracteres não é retornado pela chamada para GetStringAsync da maneira esperada. (Lembre-se de que o método já retornou uma tarefa na etapa 3.) Em vez disso, o resultado da cadeia de caracteres é armazenado na tarefa que representa a conclusão do método, getStringTask. O operador await recupera o resultado de getStringTask. A declaração de atribuição atribui o resultado recuperado a urlContents.

  8. Quando AccessTheWebAsync tem o resultado da cadeia de caracteres, o método pode calcular o comprimento da cadeia de caracteres. Em seguida, o trabalho de AccessTheWebAsync também é concluído e o manipulador de eventos em espera pode retomar. No exemplo completo no final do tópico, você pode confirmar que o manipulador de eventos recupera e imprime o valor do resultado de comprimento.

Se você é novo na programação assíncrona, reserve um minuto para considerar a diferença entre comportamento síncrono e assíncrono. Um método síncrono retorna quando seu trabalho é concluído (etapa 5), mas um método assíncrono retorna um valor de tarefa quando seu trabalho é suspenso (etapas 3 e 6). Quando o método assíncrono eventualmente conclui seu trabalho, a tarefa é marcada como concluída e o resultado, se houver, é armazenado na tarefa.

Para obter mais informações sobre fluxo de controle, consulte Fluxo de controle em programas assíncronos (Visual Basic).

Métodos assíncronos da API

Você pode estar se perguntando onde encontrar métodos como GetStringAsync que suportam programação assíncrona. O .NET Framework 4.5 ou superior contém muitos membros que trabalham com Async e Await. Você pode reconhecer esses membros pelo sufixo "Async" anexado ao nome do membro e um tipo de retorno de Task ou Task(Of TResult). Por exemplo, a classe System.IO.Stream contém métodos como CopyToAsync, ReadAsynce WriteAsync juntamente com os métodos síncronos CopyTo, Reade Write.

O Windows Runtime também contém muitos métodos que poderá usar com Async e Await em aplicações do Windows. Para obter mais informações e métodos de exemplo, consulte Chamar APIs assíncronas em C# ou Visual Basic, Programação assíncrona (aplicativos do Tempo de Execução do Windows) e WhenAny: Bridging between the .NET Framework and the Windows Runtime.

Fios

Os métodos assíncronos destinam-se a ser operações sem bloqueio. Uma expressão Await em um método assíncrono não bloqueia o thread atual enquanto a tarefa aguardada está em execução. Em vez disso, a expressão inscreve o restante do método como uma continuação e retorna o controle para o chamador do método assíncrono.

As palavras-chave Async e Await não fazem com que threads adicionais sejam criados. Os métodos assíncronos não exigem multithreading porque um método assíncrono não é executado em seu próprio thread. O método é executado no contexto de sincronização atual e usa o tempo no thread somente quando o método está ativo. Você pode usar Task.Run para mover o trabalho vinculado à CPU para um thread em segundo plano, mas um thread em segundo plano não ajuda com um processo que está apenas aguardando que os resultados fiquem disponíveis.

A abordagem baseada em assincronia para programação assíncrona é preferível às abordagens existentes em quase todos os casos. Em particular, esta abordagem é melhor do que BackgroundWorker para operações vinculadas a E/S porque o código é mais simples e você não precisa se preocupar com condições de competição. Em combinação com Task.Run, a programação assíncrona é melhor do que BackgroundWorker para operações vinculadas à CPU porque a programação assíncrona separa os detalhes de coordenação da execução do código do trabalho que Task.Run é transferido para o pool de threads.

Assíncrono e Aguarde

Se você especificar que um método é um método assíncrono usando um modificador assíncrono , habilitará os dois recursos a seguir.

  • O método assíncrono marcado pode usar Await para designar pontos de suspensão. O operador await informa ao compilador que o método async não pode continuar após esse ponto até que o processo assíncrono aguardado seja concluído. Enquanto isso, o controle é restituído ao chamador do método assíncrono.

    A suspensão de um método assíncrono numa expressão Await não constitui uma saída do método e os blocos Finally não são executados.

  • O método assíncrono marcado pode ser aguardado por métodos que o invocam.

Um método assíncrono normalmente contém uma ou mais ocorrências de um operador Await, mas a ausência de expressões Await não causa um erro de compilador. Se um método assíncrono não usar um operador Await para marcar um ponto de suspensão, o método será executado como um método síncrono, apesar do modificador Async. O compilador emite um aviso para tais métodos.

Async e Await são palavras-chave contextuais. Para obter mais informações e exemplos, consulte os seguintes tópicos:

Tipos e parâmetros de retorno

Na programação do .NET Framework, um método assíncrono normalmente retorna um Task ou um Task(Of TResult). Dentro de um método assíncrono, o operador Await é aplicado a uma tarefa retornada de uma chamada para outro método assíncrono.

Você especifica Task(Of TResult) como o tipo de retorno se o método contém uma instrução Return que especifica um operando do tipo TResult.

Você usa Task como o tipo de retorno se o método não tiver nenhuma instrução de retorno ou caso tenha uma instrução de retorno que não retorne um operando.

O exemplo a seguir mostra como você declara e chama um método que retorna um Task(Of TResult) ou um Task:

' Signature specifies Task(Of Integer)
Async Function TaskOfTResult_MethodAsync() As Task(Of Integer)

    Dim hours As Integer
    ' . . .
    ' Return statement specifies an integer result.
    Return hours
End Function

' Calls to TaskOfTResult_MethodAsync
Dim returnedTaskTResult As Task(Of Integer) = TaskOfTResult_MethodAsync()
Dim intResult As Integer = Await returnedTaskTResult
' or, in a single statement
Dim intResult As Integer = Await TaskOfTResult_MethodAsync()

' Signature specifies Task
Async Function Task_MethodAsync() As Task

    ' . . .
    ' The method has no return statement.
End Function

' Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync()
Await returnedTask
' or, in a single statement
Await Task_MethodAsync()

Cada tarefa retornada representa o trabalho contínuo. Uma tarefa encapsula informações sobre o estado do processo assíncrono e, eventualmente, o resultado final do processo ou a exceção que o processo gera se não for bem-sucedido.

Um método assíncrono também pode ser um Sub método. Esse tipo de retorno é usado principalmente para definir manipuladores de eventos, onde um tipo de retorno é necessário. Os manipuladores de eventos assíncronos geralmente servem como ponto de partida para programas assíncronos.

Um método assíncrono que é um Sub procedimento não pode ser esperado, e o chamador não pode interceptar nenhuma exceção que o método lança.

Um método assíncrono não pode declarar parâmetros ByRef , mas o método pode chamar métodos que tenham esses parâmetros.

Para obter mais informações e exemplos, consulte Tipos de retorno assíncronos (Visual Basic). Para obter mais informações sobre como capturar exceções em métodos assíncronos, consulte Tentar... Capturar... Finalmente Instrução.

As APIs assíncronas na programação do Tempo de Execução do Windows têm um dos seguintes tipos de retorno, que são semelhantes às tarefas:

Para obter mais informações e um exemplo, consulte Chamar APIs assíncronas em C# ou Visual Basic.

Convenção de nomenclatura

Por convenção, você acrescenta "Async" aos nomes dos métodos que têm um Async modificador.

Você pode ignorar a convenção em que um evento, classe base ou contrato de interface sugere um nome diferente. Por exemplo, você não deve renomear manipuladores de eventos comuns, como Button1_Click.

Tópicos e exemplos relacionados (Visual Studio)

Título Descrição Exemplo
Passo a passo: Acessando a Web usando Async e Await (Visual Basic) Mostra como converter uma solução WPF síncrona em uma solução WPF assíncrona. O aplicativo baixa uma série de sites. Exemplo de assíncrono: programação assíncrona com Async e Await (Visual Basic)
Como estender o passo a passo assíncrono usando Task.WhenAll (Visual Basic) Acrescenta Task.WhenAll ao tutorial anterior. O uso de WhenAll inicia todos os downloads ao mesmo tempo.
Como fazer várias solicitações da Web em paralelo usando Async e Await (Visual Basic) Demonstra como iniciar várias tarefas ao mesmo tempo. Exemplo assíncrono: fazer várias solicitações da Web em paralelo
Tipos de retorno assíncronos (Visual Basic) Ilustra os tipos que os métodos assíncronos podem retornar e explica quando cada tipo é apropriado.
Fluxo de controle em programas assíncronos (Visual Basic) Rastreia em detalhes o fluxo de controle através de uma sucessão de expressões de espera em um programa assíncrono. Exemplo assíncrono: Fluxo de controle em programas assíncronos
Fine-Tuning Sua Aplicação Assíncrona (Visual Basic) Mostra como adicionar a seguinte funcionalidade à sua solução assíncrona:

- Cancelar uma tarefa assíncrona ou uma lista de tarefas (Visual Basic)
- Cancelar tarefas assíncronas após um período de tempo (Visual Basic)
- Cancelar tarefas assíncronas restantes após a conclusão de uma (Visual Basic)
- Iniciar várias tarefas assíncronas e processá-las à medida que forem concluídas (Visual Basic)
Exemplo de assíncrono: ajustando seu aplicativo
Manipulando a reentrância em aplicativos assíncronos (Visual Basic) Mostra como lidar com casos em que uma operação assíncrona ativa é reiniciada enquanto está em execução.
WhenAny: Fazendo a ponte entre o .NET Framework e o Tempo de Execução do Windows Mostra como construir uma ponte entre os tipos de tarefa no .NET Framework e IAsyncOperations no Tempo de Execução do Windows, de forma a permitir a utilização de WhenAny com um método do Tempo de Execução do Windows. Assíncrono Exemplo: Ligação entre o .NET e o Windows Runtime (AsTask e WhenAny)
Cancelamento assíncrono: Ponte entre o .NET Framework e o Tempo de Execução do Windows Mostra como construir uma ponte entre os tipos de tarefa no .NET Framework e IAsyncOperations no Tempo de Execução do Windows, de forma a permitir a utilização de CancellationTokenSource com um método do Tempo de Execução do Windows. Amostra Assíncrona: Ligação entre .NET e o Runtime do Windows (AsTask & Cancelamento)
Usando o Async para acesso a arquivos (Visual Basic) Lista e demonstra os benefícios de usar async e await para acessar arquivos.
Padrão assíncrono baseado em tarefas (TAP) Descreve um novo padrão para assincronia no .NET Framework. O padrão é baseado nos tipos Task e Task(Of TResult).

Exemplo completo

O código a seguir é o arquivo de MainWindow.xaml.vb do aplicativo Windows Presentation Foundation (WPF) que este tópico aborda. Você pode descarregar o exemplo de Async Sample: Exemplo de "Programação Assíncrona com Async e Await".


Imports System.Net.Http

' Example that demonstrates Asynchronous Programming with Async and Await.
' It uses HttpClient.GetStringAsync to download the contents of a website.
' Sample Output:
' Working . . . . . . .
'
' Length of the downloaded string: 39678.

Class MainWindow

    ' Mark the event handler with Async so you can use Await in it.
    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

        ' Call and await immediately.
        ' StartButton_Click suspends until AccessTheWebAsync is done.
        Dim contentLength As Integer = Await AccessTheWebAsync()

        ResultsTextBox.Text &= $"{vbCrLf}Length of the downloaded string: {contentLength}.{vbCrLf}"

    End Sub


    ' Three things to note about writing an Async Function:
    '  - The function has an Async modifier.
    '  - Its return type is Task or Task(Of T). (See "Return Types" section.)
    '  - As a matter of convention, its name ends in "Async".
    Async Function AccessTheWebAsync() As Task(Of Integer)

        Using client As New HttpClient()

            ' Call and await separately.
            '  - AccessTheWebAsync can do other things while GetStringAsync is also running.
            '  - getStringTask stores the task we get from the call to GetStringAsync.
            '  - Task(Of String) means it is a task which returns a String when it is done.
            Dim getStringTask As Task(Of String) =
                client.GetStringAsync("https://learn.microsoft.com/dotnet")

            ' You can do other work here that doesn't rely on the string from GetStringAsync.
            DoIndependentWork()

            ' The Await operator suspends AccessTheWebAsync.
            '  - AccessTheWebAsync does not continue until getStringTask is complete.
            '  - Meanwhile, control returns to the caller of AccessTheWebAsync.
            '  - Control resumes here when getStringTask is complete.
            '  - The Await operator then retrieves the String result from getStringTask.
            Dim urlContents As String = Await getStringTask

            ' The Return statement specifies an Integer result.
            ' A method which awaits AccessTheWebAsync receives the Length value.
            Return urlContents.Length

        End Using

    End Function

    Sub DoIndependentWork()
        ResultsTextBox.Text &= $"Working . . . . . . .{vbCrLf}"
    End Sub

End Class

Ver também