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 chamaAccessTheWebAsync
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.
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.
Descompacte o arquivo baixado e inicie o Visual Studio.
Na barra de menus, escolha Arquivo, Abrir, Projeto/Solução.
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:
Inicie o Visual Studio.
Na barra de menus, escolha Arquivo, Novo, Projeto.
A caixa de diálogo Novo projeto é aberta.
No painel Modelos Instalados, escolha Visual Basic e, em seguida, escolha Aplicativo WPF na lista de tipos de projeto.
Digite
AsyncTracer
como o nome do projeto e, em seguida, escolha o botão OK .O novo projeto aparece no Gerenciador de Soluções.
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.
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.
Adicione uma referência para System.Net.Http.
No Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb e escolha Exibir Código.
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
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 AccessTheWebAsync
e AccessTheWebAsync
chamam o método GetStringAsync(String)assíncronoHttpClient. A imagem a seguir descreve as chamadas de método para método.
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 AccessTheWebAsync
client.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://learn.microsoft.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.
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://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 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.
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.
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
.