Compartilhar via


Fluxo de controle em programas assíncronos (Visual Basic)

Você pode escrever e manter programas assíncronos mais facilmente usando as palavras-chave Async e Await. No entanto, os resultados podem surpreendê-lo se você não entender o funcionamento do seu programa. Este tópico rastreia o fluxo de controle por meio de um programa assíncrono simples para mostrar quando o controle se move de um método para o outro e quais informações são transferidas a cada vez.

Observação

As palavras-chave Async e Await foram introduzidas no Visual Studio 2012.

Em geral, você marca métodos que contêm código assíncrono com o modificador Async. Em um método marcado com um modificador assíncrono, você pode usar um operador Await (Visual Basic) para especificar o local em que o método faz uma pausa para esperar a conclusão de um processo assíncrono chamado. Para obter mais informações, consulte Programação Assíncrona com Async e Await (Visual Basic).

O exemplo a seguir usa os métodos assíncronos para baixar o conteúdo de um site especificado como uma cadeia de caracteres e exibir o comprimento da cadeia de caracteres. O exemplo contém os dois métodos a seguir.

  • startButton_Click, que chama AccessTheWebAsync e exibe o resultado.

  • AccessTheWebAsync, que baixa o conteúdo de um site na forma de uma cadeia de caracteres e retorna o comprimento da cadeia de caracteres. AccessTheWebAsync usa um método HttpClient assíncrono, GetStringAsync(String), para baixar o conteúdo.

Linhas numeradas de exibição aparecem em pontos estratégicos em todo o programa para ajudá-lo a entender como o programa é executado e explicar o que acontece em cada ponto marcado. As linhas de exibição são rotuladas como "ONE" até "SIX." Os rótulos representam a ordem na qual o programa atinge essas linhas de código.

O código a seguir mostra uma estrutura de tópicos do programa.

Class MainWindow

    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click

        ' ONE
        Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

        ' FOUR
        Dim contentLength As Integer = Await getLengthTask

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

    End Sub

    Async Function AccessTheWebAsync() As Task(Of Integer)

        ' TWO
        Dim client As HttpClient = New HttpClient()
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com")

        ' THREE
        Dim urlContents As String = Await getStringTask

        ' FIVE
        Return urlContents.Length
    End Function

End Class

Cada um dos locais rotulados, "UM"a "SEIS," exibe informações sobre o estado atual do programa. A seguinte saída é produzida:

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.

TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Length of the downloaded string: 33946.

Configurar o Programa

Você pode baixar o código usado nesse tópico no MSDN ou você mesmo pode criá-lo.

Observação

Para executar o exemplo, você deve ter o Visual Studio 2012 ou mais recente e o .NET Framework 4.5 ou posterior instalados no seu computador.

Baixar o Programa

Você pode baixar o aplicativo deste tópico em Exemplo assíncrono: controlar fluxo em programas assíncronos. As etapas a seguir abrem e executam o programa.

  1. Descompacte o arquivo baixado e, em seguida, inicie o Visual Studio.

  2. Na barra de menus, escolha Arquivo, Abrir, Projeto/Solução.

  3. Navegue até a pasta que contém o código de exemplo descompactado, abra o arquivo da solução (.sln) e, em seguida, escolha a tecla F5 para compilar e executar o projeto.

Compilar o Programa Sozinho

O projeto WPF (Windows Presentation Foundation) a seguir contém o exemplo de código deste tópico.

Para executar o projeto, realize as seguintes etapas:

  1. Inicie o Visual Studio.

  2. Na barra de menus, escolha Arquivo, Novo, Projeto.

    A caixa de diálogo Novo Projeto será aberta.

  3. No painel Modelos Instalados, selecione Visual Basic e Aplicativo WPF na lista de tipos de projeto.

  4. Digite AsyncTracer como o nome do projeto e, em seguida, escolha o botão OK.

    O novo projeto aparece no Gerenciador de Soluções.

  5. No Editor do Visual Studio Code, escolha a guia MainWindow.xaml.

    Se a guia não estiver visível, abra o menu de atalho para MainWindow.xaml no Gerenciador de Soluções e, em seguida, escolha Exibir Código.

  6. Na exibição XAML de MainWindow.xaml, substitua o código pelo código a seguir.

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
        Title="Control Flow Trace" Height="350" Width="525">
        <Grid>
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top" Width="75"/>
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="510" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" d:LayoutOverrides="HorizontalMargin"/>
    
        </Grid>
    </Window>
    

    Uma janela simples, contendo uma caixa de texto e um botão, aparecerá no modo de exibição de Design de MainWindow.xaml.

  7. Adicione uma referência para System.Net.Http.

  8. No Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb e selecione Exibir Código.

  9. Em MainWindow.xaml.vb, substitua o código pelo código a seguir.

    ' Add an Imports statement and a reference for System.Net.Http.
    Imports System.Net.Http
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
    
            ' The display lines in the example lead you through the control shifts.
            ResultsTextBox.Text &= "ONE:   Entering StartButton_Click." & vbCrLf &
                "           Calling AccessTheWebAsync." & vbCrLf
    
            Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
    
            ResultsTextBox.Text &= vbCrLf & "FOUR:  Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is started." & vbCrLf &
                "           About to await getLengthTask -- no caller to return to." & vbCrLf
    
            Dim contentLength As Integer = Await getLengthTask
    
            ResultsTextBox.Text &= vbCrLf & "SIX:   Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is finished." & vbCrLf &
                "           Result from AccessTheWebAsync is stored in contentLength." & vbCrLf &
                "           About to display contentLength and exit." & vbCrLf
    
            ResultsTextBox.Text &=
                String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
        End Sub
    
        Async Function AccessTheWebAsync() As Task(Of Integer)
    
            ResultsTextBox.Text &= vbCrLf & "TWO:   Entering AccessTheWebAsync."
    
            ' Declare an HttpClient object.
            Dim client As HttpClient = New HttpClient()
    
            ResultsTextBox.Text &= vbCrLf & "           Calling HttpClient.GetStringAsync." & vbCrLf
    
            ' GetStringAsync returns a Task(Of String).
            Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")
    
            ResultsTextBox.Text &= vbCrLf & "THREE: Back in AccessTheWebAsync." & vbCrLf &
                "           Task getStringTask is started."
    
            ' AccessTheWebAsync can continue to work until getStringTask is awaited.
    
            ResultsTextBox.Text &=
                vbCrLf & "           About to await getStringTask & return a Task(Of Integer) to StartButton_Click." & vbCrLf
    
            ' Retrieve the website contents when task is complete.
            Dim urlContents As String = Await getStringTask
    
            ResultsTextBox.Text &= vbCrLf & "FIVE:  Back in AccessTheWebAsync." &
                vbCrLf & "           Task getStringTask is complete." &
                vbCrLf & "           Processing the return statement." &
                vbCrLf & "           Exiting from AccessTheWebAsync." & vbCrLf
    
            Return urlContents.Length
        End Function
    
    End Class
    
  10. Escolha a tecla F5 para executar o programa e, em seguida, o botão Iniciar.

    Deverá aparecer esta saída:

    ONE:   Entering startButton_Click.
               Calling AccessTheWebAsync.
    
    TWO:   Entering AccessTheWebAsync.
               Calling HttpClient.GetStringAsync.
    
    THREE: Back in AccessTheWebAsync.
               Task getStringTask is started.
               About to await getStringTask & return a Task<int> to startButton_Click.
    
    FOUR:  Back in startButton_Click.
               Task getLengthTask is started.
               About to await getLengthTask -- no caller to return to.
    
    FIVE:  Back in AccessTheWebAsync.
               Task getStringTask is complete.
               Processing the return statement.
               Exiting from AccessTheWebAsync.
    
    SIX:   Back in startButton_Click.
               Task getLengthTask is finished.
               Result from AccessTheWebAsync is stored in contentLength.
               About to display contentLength and exit.
    
    Length of the downloaded string: 33946.
    

Rastrear o Programa

Etapas UM e DOIS

As duas primeiras linhas de exibição rastreiam o caminho conforme startButton_Click chama AccessTheWebAsync e AccessTheWebAsync chama o método HttpClient assíncrono GetStringAsync(String). A imagem a seguir delineia as chamadas de método a método.

Steps ONE and TWO

O tipo de retorno de ambos AccessTheWebAsync e client.GetStringAsync é Task<TResult>. Para AccessTheWebAsync, TResult é um inteiro. Para GetStringAsync, TResult é uma cadeia de caracteres. Para obter mais informações sobre tipos de retorno de método assíncrono, consulte Tipos de Retorno Assíncronos (Visual Basic).

Um método assíncrono de retorno de tarefas retorna uma instância de tarefa, quando o controle volta para o chamador. O controle retorna de um método assíncrono para seu chamador quando um operador Await é encontrado no método chamado ou quando o método chamado termina. As linhas de exibição rotuladas como "TRÊS"até "SEIS" rastreiam essa parte do processo.

Etapa TRÊS

Em AccessTheWebAsync, o método assíncrono GetStringAsync(String) é chamado para baixar o conteúdo de página da Web de destino. O controle retorna de client.GetStringAsync para AccessTheWebAsync quando client.GetStringAsync retornar.

O método client.GetStringAsync retorna uma tarefa de cadeia de caracteres que é atribuída à variável getStringTask em AccessTheWebAsync. A linha do programa de exemplo a seguir mostra a chamada à client.GetStringAsync e a atribuição.

Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")

Você pode imaginar a tarefa como uma promessa de client.GetStringAsync para eventualmente produzir uma cadeia de caracteres real. Enquanto isso, se AccessTheWebAsync tiver trabalho a fazer que não dependa da cadeia de caracteres prometida de client.GetStringAsync, esse trabalho poderá prosseguir enquanto client.GetStringAsync aguarda. No exemplo, as linhas de saída seguintes, que são rotuladas como "TRÊS", representam a oportunidade para fazer o trabalho independente

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

A instrução a seguir suspende o andamento em AccessTheWebAsync quando getStringTask é aguardado.

Dim urlContents As String = Await getStringTask

A imagem a seguir mostra o fluxo de controle de client.GetStringAsync para a atribuição a getStringTask e da criação de getStringTask para a aplicação de um operador Await.

Step THREE

A expressão await suspende AccessTheWebAsync até que client.GetStringAsync retorne. Enquanto isso, o controle retorna para o chamador de AccessTheWebAsync, startButton_Click.

Observação

Normalmente, você aguarda a chamada a um método assíncrono imediatamente. Por exemplo, a atribuição a seguir poderia substituir o código anterior que cria e, em seguida, aguarda getStringTask: Dim urlContents As String = Await client.GetStringAsync("https://learn.microsoft.com")

Neste tópico, o operador await é aplicado posteriormente para acomodar as linhas de saída que marcam o fluxo de controle em todo o programa.

Etapa QUATRO

O tipo de retorno declarado de AccessTheWebAsync é Task(Of Integer). Portanto, quando AccessTheWebAsync for suspenso, ele retornará uma tarefa de inteiro para startButton_Click. Você deve compreender que a tarefa retornada não é getStringTask. A tarefa retornada é uma nova tarefa de um inteiro que representa o que ainda precisa ser feito no método suspenso AccessTheWebAsync. A tarefa é uma promessa de AccessTheWebAsync para produzir um inteiro quando a tarefa for concluída.

A instrução a seguir atribui essa tarefa à variável getLengthTask.

Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

Como em AccessTheWebAsync, startButton_Click pode continuar com o trabalho que não dependa dos resultados da tarefa assíncrona (getLengthTask) até que a tarefa seja aguardada. As seguintes linhas de saída representam esse trabalho:

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

O avanço em startButton_Click será suspenso quando getLengthTask for aguardada. A seguinte instrução de atribuição suspende startButton_Click até que AccessTheWebAsync seja concluída.

Dim contentLength As Integer = Await getLengthTask

Na ilustração a seguir, as setas mostram o fluxo de controle da expressão await em AccessTheWebAsync para a atribuição de um valor para getLengthTask, seguido pelo processamento normal de startButton_Click até que getLengthTask seja aguardada.

Step FOUR

Etapa CINCO

Quando client.GetStringAsync sinaliza que está concluído, o processamento em AccessTheWebAsync é liberado da suspensão e pode continuar após a instrução await. As seguintes linhas de saída representam a retomada do processamento:

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

O operando da instrução de retorno, urlContents.Length, é armazenado na tarefa que AccessTheWebAsync retorna. A expressão await recupera esse valor de getLengthTask em startButton_Click.

A imagem a seguir mostra a transferência de controle após client.GetStringAsync (e getStringTask) estarem concluídas.

Step FIVE

AccessTheWebAsync é executado até a conclusão e o controle retorna ao startButton_Click, que está aguardando a conclusão.

Etapa SEIS

Quando AccessTheWebAsync sinaliza que está concluído, o processamento pode continuar após a instrução await em startButton_Async. Na verdade, o programa não tem mais nada para fazer.

As seguintes linhas de saída representam a retomada do processamento em startButton_Async:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

A expressão await recupera de getLengthTask o valor inteiro, que é o operando da instrução return em AccessTheWebAsync. A instrução a seguir atribui esse valor à variável contentLength.

Dim contentLength As Integer = Await getLengthTask

A imagem a seguir mostra o retorno do controle de AccessTheWebAsync para startButton_Click.

Step SIX

Confira também