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.

Nota

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://video2.skills-academy.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.

Nota

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. As etapas a seguir 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://video2.skills-academy.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 rastreiam o caminho como startButton_Click chamadas AccessTheWebAsynce AccessTheWebAsync chamam o método GetStringAsync(String)assíncronoHttpClient. A imagem a seguir descreve as chamadas de método para 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 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 seu chamador quando um Await operador é 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 quando retorna AccessTheWebAsyncclient.GetStringAsync .

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://video2.skills-academy.com")

Você pode pensar na tarefa como uma promessa client.GetStringAsync , produzindo uma cadeia de caracteres real eventualmente. 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.

A declaração a seguir suspende o progresso em AccessTheWebAsync quando getStringTask é aguardado.

Dim urlContents As String = Await getStringTask

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

Step THREE

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

Nota

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://video2.skills-academy.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 AccessTheWebAsync é suspenso, ele retorna uma tarefa de inteiro para startButton_Click. Você deve entender que a tarefa retornada não getStringTaské . A tarefa retornada é uma nova tarefa de inteiro que representa o que resta a ser feito no método suspenso, AccessTheWebAsync. A tarefa é uma promessa de AccessTheWebAsync produzir um inteiro quando a tarefa é 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 instrução de atribuição a seguir é startButton_Click suspensa até AccessTheWebAsync ser concluída.

Dim contentLength As Integer = Await getLengthTask

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

Step FOUR

Passo CINCO

Quando client.GetStringAsync sinaliza que está concluído, o processamento é AccessTheWebAsync liberado da suspensão e pode continuar após a declaração de espera. 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.

Step FIVE

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 recupera getLengthTask do 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.

Step SIX

Consulte também