Partilhar via


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

Você pode escrever e manter programas assíncronos mais facilmente usando as Async palavras-chave e Await . No entanto, os resultados podem surpreendê-lo se você não entender como seu programa funciona. 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 outro e quais informações são transferidas a cada vez.

Observação

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

Em geral, você marca métodos que contêm código assíncrono com o modificador Assíncrono . Em um método marcado com um modificador assíncrono, você pode usar um operador Await (Visual Basic) para especificar onde o método pausa para aguardar 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 métodos async para baixar o conteúdo de um site especificado como uma cadeia de caracteres e para 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 como uma cadeia de caracteres e retorna o comprimento da cadeia de caracteres. AccessTheWebAsync usa um método assíncrono HttpClient , GetStringAsync(String), para baixar o conteúdo.

Linhas de exibição numeradas aparecem em pontos estratégicos ao longo do 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 de "UM" a "SEIS". Os rótulos representam a ordem em que o programa atinge essas linhas de código.

O código a seguir mostra um esboço 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 que este tópico usa do MSDN ou criá-lo você mesmo.

Observação

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

Faça o download do programa

Você pode baixar o aplicativo para este tópico em Async Sample: Control Flow in Async Programs. Os passos seguintes abrem e executam o programa.

  1. Descompacte o arquivo baixado e 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 de solução (.sln) e escolha a tecla F5 para criar e executar o projeto.

Construa o programa você mesmo

O seguinte projeto do Windows Presentation Foundation (WPF) contém o exemplo de código para este tópico.

Para executar o projeto, execute as seguintes etapas:

  1. Inicie o Visual Studio.

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

    A caixa de diálogo Novo projeto é aberta.

  3. No painel Modelos Instalados , escolha Visual Basic e, em seguida, escolha 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 de Códigos do Visual Studio, 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 escolha View Code.

  6. No modo de 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 que contém uma caixa de texto e um botão aparece no modo 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 escolha Exibir Código.

  9. No 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, escolha o botão Iniciar .

    A seguinte saída deve aparecer:

    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

Passos UM e DOIS

As duas primeiras linhas de exibição traçam o caminho como startButton_Click chama AccessTheWebAsync, e AccessTheWebAsync chama o método assíncrono HttpClientGetStringAsync(String). A imagem seguinte ilustra as chamadas de um método para outro método.

Passos UM e DOIS

O tipo de retorno de ambos AccessTheWebAsync e client.GetStringAsync é Task<TResult>. Para AccessTheWebAsync, TResult é um inteiro. Para GetStringAsync, TResult é uma string. 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 tarefa retorna uma instância de tarefa quando o controle volta para o chamador. O controle retorna de um método assíncrono para o seu chamador, quando o operador Await é encontrado no método chamado, ou quando o método chamado termina. As linhas de exibição rotuladas de "TRÊS" a "SEIS" rastreiam essa parte do processo.

Passo TRÊS

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

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

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

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

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

O avanço em AccessTheWebAsync é suspenso enquanto se aguarda getStringTask.

Dim urlContents As String = Await getStringTask

A imagem a seguir mostra o fluxo de controle de client.GetStringAsync para a atribuição em getStringTask e desde a criação de getStringTask até à aplicação de um operador Await.

Passo TRÊS

A expressão await suspende AccessTheWebAsync até que client.GetStringAsync retorne. Entretanto, o controle é devolvido ao chamador de AccessTheWebAsync, startButton_Click.

Observação

Normalmente, você aguarda a chamada para um método assíncrono imediatamente. Por exemplo, a seguinte atribuição pode 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 através do programa.

Passo QUATRO

O tipo de retorno declarado de AccessTheWebAsync é Task(Of Integer). Portanto, quando o elemento AccessTheWebAsync é suspenso, retorna uma tarefa de número inteiro para startButton_Click. Você deve entender que a tarefa retornada não está getStringTask. A tarefa retornada é uma nova tarefa que envolve um valor inteiro, representando o que ainda falta fazer no método suspenso, AccessTheWebAsync. A tarefa é uma promessa de AccessTheWebAsync para produzir um número inteiro quando for concluída.

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

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

Como no AccessTheWebAsync, startButton_Click pode continuar com o trabalho que não depende 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 progresso em startButton_Click é suspenso quando getLengthTask é aguardado. A seguinte instrução de atribuição suspende startButton_Click até AccessTheWebAsync estar 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 em startButton_Click até que getLengthTask seja aguardado.

Passo QUATRO

Passo CINCO

Quando client.GetStringAsync sinaliza que está concluído, o processamento em AccessTheWebAsync é retomado 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 return, 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) serem concluídas.

Passo CINCO

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

Passo 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 a 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 obtém do getLengthTask o valor inteiro que é o operando da instrução return em AccessTheWebAsync. A instrução a seguir atribui esse valor à contentLength variável.

Dim contentLength As Integer = Await getLengthTask

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

Passo SIX

Ver também