Fluxo de controle em programas Async (C# e Visual Basic)
Você pode escrever e manter programas assíncronos mais facilmente usando as palavras-chave de Async e de Await .No entanto, os resultados podem surpreendê-lo se você não entende como seu programa opera.Este tópico rastreamento do fluxo de controle com um programa simples de async para mostrar quando o controle se move um método da outra e informações que está transferida cada vez.
Observação |
---|
As palavras-chave de Async e de Await foram introduzidos no Visual Studio 2012.Para obter mais informações sobre novos recursos nessa versão, consulte Novidades no Visual Studio 2012. |
Geralmente, você marca métodos que contêm o código assíncrona com o modificador de Async (Visual Basic) ou de async (C#) .Em um método que foi marcado com um modificador de async, você pode usar um operador de Espere (Visual Basic) ou de espere (C#) para especificar onde o método pausa para aguardar um processo assíncrono chamado para concluir.Para obter mais informações, consulte Programação com Async assíncrona e esperar (C# e Visual Basic).
O exemplo a seguir usa métodos de async para baixar o conteúdo de um site especificada como uma cadeia de caracteres e exibir o comprimento da cadeia de caracteres.O exemplo contém os dois seguintes métodos.
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íncrona de HttpClient , GetStringAsync(String), para baixar o conteúdo.
As linhas de exibição numeradas aparecem em pontos estratégicos em todo o programa para ajudar você a entender como as executa o programa e a explicar o que acontece em cada ponto que é marcado.As linhas de exibição são rotuladas “UMA” com “SEIS”. Rótulos representam a ordem na qual o programa atinge essas linhas de código.
O código a seguir mostra um estrutura 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 &=
String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
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://msdn.microsoft.com")
' THREE
Dim urlContents As String = Await getStringTask
' FIVE
Return urlContents.Length
End Function
End Class
public partial class MainWindow : Window
{
// . . .
private async void startButton_Click(object sender, RoutedEventArgs e)
{
// ONE
Task<int> getLengthTask = AccessTheWebAsync();
// FOUR
int contentLength = await getLengthTask;
// SIX
resultsTextBox.Text +=
String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
}
async Task<int> AccessTheWebAsync()
{
// TWO
HttpClient client = new HttpClient();
Task<string> getStringTask =
client.GetStringAsync("https://msdn.microsoft.com");
// THREE
string urlContents = await getStringTask;
// FIVE
return urlContents.Length;
}
}
Cada um dos locais rotuladas “, UM” com “SEIS,” exibe informações sobre o estado atual do programa.A saída a seguir são geradas.
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 você pode compilá-lo você mesmo.
Observação |
---|
Para executar o exemplo, você deve ter o Visual Studio 2012, o Visual Studio Express 2012 ou o .NET Framework 4.5 instalado no computador. |
Baixar o programa
Você pode baixar o aplicativo para de Exemplo de Async: Fluxo de controle em programas de Asynceste tópico.As etapas a seguir abre e executar o programa.
Abra o zíper o arquivo baixado, e comece em Visual Studio 2012.
Na barra de menu, escolha Arquivo, Abrir, Projeto/solução.
Navegue até a pasta que contém o código aberto o zíper de exemplo, abra o arquivo de solução (.sln), e escolha a tecla F5 para compilar e executar o projeto.
Compilar o programa você mesmo
O seguinte projeto 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 menu, escolha Arquivo, Novo, Projeto.
A Caixa de diálogo Novo Projeto é exibida.
No painel de Modelos Instalados , escolha Visual Basic ou Visual C#, escolha Aplicativo WPF da lista de tipos de projeto.
Entre em AsyncTracer como o nome do projeto, e clique no botão de 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 em Gerenciador de Soluções, e escolha Exibir Código.
No modo de XAML de MainWindow.xaml, substitua o código com o código a seguir.
<Window xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://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>
<Window xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="https://schemas.microsoft.com/expression/blend/2008" xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="AsyncTracer.MainWindow" Title="Control Flow Trace" Height="350" Width="592"> <Grid> <Button x:Name="startButton" Content="Start
" HorizontalAlignment="Left" Margin="250,10,0,0" VerticalAlignment="Top" Width="75" Height="24" Click="startButton_Click" d:LayoutOverrides="GridBox"/> <TextBox x:Name="resultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="576" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" Grid.ColumnSpan="3"/> </Grid> </Window>
Uma janela simples que contém uma caixa de texto e um botão aparece no modo de Design de MainWindow.xaml.
Adicione uma referência para System.Net.Http.
Em Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb ou MainWindow.xaml.cs, escolha Exibir Código.
Em MainWindow.xaml.vb ou em MainWindow.xaml.cs, substitua o código com o 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://msdn.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
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; // Add a using directive and a reference for System.Net.Http; using System.Net.Http; namespace AsyncTracer { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private async void startButton_Click(object sender, RoutedEventArgs e) { // The display lines in the example lead you through the control shifts. resultsTextBox.Text += "ONE: Entering startButton_Click.\r\n" + " Calling AccessTheWebAsync.\r\n"; Task<int> getLengthTask = AccessTheWebAsync(); resultsTextBox.Text += "\r\nFOUR: Back in startButton_Click.\r\n" + " Task getLengthTask is started.\r\n" + " About to await getLengthTask -- no caller to return to.\r\n"; int contentLength = await getLengthTask; resultsTextBox.Text += "\r\nSIX: Back in startButton_Click.\r\n" + " Task getLengthTask is finished.\r\n" + " Result from AccessTheWebAsync is stored in contentLength.\r\n" + " About to display contentLength and exit.\r\n"; resultsTextBox.Text += String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength); } async Task<int> AccessTheWebAsync() { resultsTextBox.Text += "\r\nTWO: Entering AccessTheWebAsync."; // Declare an HttpClient object. HttpClient client = new HttpClient(); resultsTextBox.Text += "\r\n Calling HttpClient.GetStringAsync.\r\n"; // GetStringAsync returns a Task<string>. Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com"); resultsTextBox.Text += "\r\nTHREE: Back in AccessTheWebAsync.\r\n" + " Task getStringTask is started."; // AccessTheWebAsync can continue to work until getStringTask is awaited. resultsTextBox.Text += "\r\n About to await getStringTask and return a Task<int> to startButton_Click.\r\n"; // Retrieve the website contents when task is complete. string urlContents = await getStringTask; resultsTextBox.Text += "\r\nFIVE: Back in AccessTheWebAsync." + "\r\n Task getStringTask is complete." + "\r\n Processing the return statement." + "\r\n Exiting from AccessTheWebAsync.\r\n"; return urlContents.Length; } } }
Escolha a tecla F5 para executar o programa, escolha o botão de Iniciar .
As seguintes saída devem 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.
Rastreamento o programa
Etapas UMA e DOIS
As duas primeiras linhas de exibição mostra o caminho como startButton_Click chama AccessTheWebAsync, e chamadas de AccessTheWebAsync o método assíncrono GetStringAsync(String)de HttpClient .A imagem a seguir descreve as chamadas de método para o método.
O tipo de retorno de ambos AccessTheWebAsync e client.GetStringAsync é Task<TResult>.Para AccessTheWebAsync, TResult é um número inteiro.Para GetStringAsync, TResult é uma cadeia de caracteres.Para obter mais informações sobre tipos de retorno do método de async, consulte Tipos de retorno de Async (C# e Visual Basic).
Um método chave retornando de async retorna uma instância de tarefa quando o controle desloca de volta para o chamador.O controle retorna de um método de async para o chamador qualquer pessoa quando um operador de Await ou de await está localizado no método chamado ou quando o método chamado termina.As linhas de exibição que são rotuladas “TRÊS” a “SEIS” rastreamentos esta parte do processo.
Etapa TRÊS
Em AccessTheWebAsync, o método assíncrono GetStringAsync(String) é chamado para baixar o conteúdo da Web page de destino.O controle retorna client.GetStringAsync da AccessTheWebAsync quando client.GetStringAsync retorna.
O método de client.GetStringAsync retorna uma tarefa de cadeia de caracteres que é atribuído à variável de getStringTask em AccessTheWebAsync.A seguinte linha no programa do exemplo mostra a chamada a client.GetStringAsync e a atribuição.
Dim getStringTask As Task(Of String) = client.GetStringAsync("https://msdn.microsoft.com")
Task<string> getStringTask = client.GetStringAsync("https://msdn.microsoft.com");
Você pode ver a tarefa como uma promessa por client.GetStringAsync de gerar se houver uma cadeia de caracteres real.Entretanto, se AccessTheWebAsync tem a fazer o trabalho que não depende da cadeia de caracteres prometida de client.GetStringAsync, o trabalho pode continuar quando client.GetStringAsync esperar.No exemplo, as seguintes linhas de saída, que são rotuladas TRÊS “,” representam a oportunidade para fazer o trabalho de forma independente
THREE: Back in AccessTheWebAsync.
Task getStringTask is started.
About to await getStringTask & return a Task<int> to startButton_Click.
A seguinte declaração suspende o progresso em AccessTheWebAsync quando getStringTask é esperado.
Dim urlContents As String = Await getStringTask
string urlContents = await getStringTask;
A imagem a seguir mostra o fluxo de controle de client.GetStringAsync a atribuição a getStringTask e de criação de getStringTask para o aplicativo de um operador de espera.
A expressão de espera suspende AccessTheWebAsync até que client.GetStringAsync retorna.Entretanto, o controle retorna para o chamador de AccessTheWebAsync, startButton_Click.
Observação |
---|
Normalmente, você espera a chamada a um método assíncrono imediatamente.Por exemplo, uma das atribuições pode substituir o código anterior que cria e espera em getStringTask:
Neste tópico, o operador de espera é aplicado depois para acomodar as linhas de saída que marcam o fluxo de controle com o programa. |
Etapa QUATRO
O tipo de retorno de AccessTheWebAsync é declarado Task(Of Integer) no Visual Basic e em Task<int> C#.Portanto, quando AccessTheWebAsync é suspenso, retorna uma tarefa inteiro a startButton_Click.Você deve compreender que a tarefa retornado não é getStringTask.A tarefa retornado é uma nova tarefa inteiro que representa o que resta ser feito no método suspenso, AccessTheWebAsync.A tarefa é uma promessa de AccessTheWebAsync de gerar um inteiro quando a tarefa é concluída.
A instrução a seguir atribui esta tarefa à variável de getLengthTask .
Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
Task<int> getLengthTask = AccessTheWebAsync();
Como em AccessTheWebAsync, startButton_Click pode continuar com trabalho que não depende dos resultados da tarefa assíncrono (getLengthTask) até que a tarefa é esperada.As seguintes linhas de saída representam este trabalho.
FOUR: Back in startButton_Click.
Task getLengthTask is started.
About to await getLengthTask -- no caller to return to.
O progresso em startButton_Click é suspensa quando getLengthTask é esperado.A seguinte instrução de atribuição suspende startButton_Click até que AccessTheWebAsync seja concluída.
Dim contentLength As Integer = Await getLengthTask
int contentLength = await getLengthTask;
Na ilustração a seguir, as setas mostra o fluxo de controle da expressão de espera em AccessTheWebAsync a atribuição de um valor a getLengthTask, seguido pelo processamento normal em startButton_Click até que getLengthTask é esperado.
Etapa CINCO
Quando client.GetStringAsync sinaliza que estiver concluída, processando em AccessTheWebAsync é liberado de suspensão e pode continuar o passado a declaração de espera.As seguintes linhas de saída representam a ressunção de processamento.
FIVE: Back in AccessTheWebAsync.
Task getStringTask is complete.
Processing the return statement.
Exiting from AccessTheWebAsync.
O operando da declaração de retorno, urlContents.Length, é armazenado na tarefa que AccessTheWebAsync retorna.A expressão de espera recupera o valor de getLengthTask em startButton_Click.
A imagem a seguir mostra a transferência de controle após client.GetStringAsync (e getStringTask) é concluída.
AccessTheWebAsync executa a conclusão, e o controle retorna a startButton_Click, que está aguardando a conclusão.
Etapa SEIS
Quando AccessTheWebAsync sinaliza que estiver concluída, processar pode continuar o passado a declaração de espera em startButton_Async.Na verdade, o programa não tem nada mais fazer.
As seguintes linhas de saída representam a ressunção de 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 de espera de getLengthTask recupera o valor inteiro que é o operando de declaração return em AccessTheWebAsync.A instrução a seguir atribui o valor à variável de contentLength .
Dim contentLength As Integer = Await getLengthTask
int contentLength = await getLengthTask;
A imagem a seguir mostra o retorno de controle de AccessTheWebAsync a startButton_Click.
Consulte também
Tarefas
Passo a passo: Acessando a Web usando Async e aguardar (C# e Visual Basic)
Passo a passo: Usando o depurador com métodos assíncronos
Conceitos
Programação com Async assíncrona e esperar (C# e Visual Basic)
Tipos de retorno de Async (C# e Visual Basic)
Outros recursos
Exemplo de Async: Fluxo de controle em programas de Async (C# e Visual Basic)