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çãoObservaçã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çãoObservaçã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.

Hh873191.collapse_all(pt-br,VS.110).gifBaixar 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.

  1. Abra o zíper o arquivo baixado, e comece em Visual Studio 2012.

  2. Na barra de menu, escolha Arquivo, Abrir, Projeto/solução.

  3. 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.

Hh873191.collapse_all(pt-br,VS.110).gifCompilar 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:

  1. Inicie o Visual Studio.

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

    A Caixa de diálogo Novo Projeto é exibida.

  3. No painel de Modelos Instalados , escolha Visual Basic ou Visual C#, escolha Aplicativo WPF da lista de tipos de projeto.

  4. Entre em AsyncTracer como o nome do projeto, e clique no botão de 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 em Gerenciador de Soluções, e escolha Exibir Código.

  6. 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&#xa;" 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.

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

  8. Em Gerenciador de Soluções, abra o menu de atalho para MainWindow.xaml.vb ou MainWindow.xaml.cs, escolha Exibir Código.

  9. 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;
            }
        }
    }
    
  10. 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

Hh873191.collapse_all(pt-br,VS.110).gifEtapas 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.

Etapas um e dois

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.

Hh873191.collapse_all(pt-br,VS.110).gifEtapa 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.

Etapa três

A expressão de espera suspende AccessTheWebAsync até que client.GetStringAsync retorna.Entretanto, o controle retorna para o chamador de AccessTheWebAsync, startButton_Click.

ObservaçãoObservaçã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:

  • Visual Basic: Dim urlContents As String = Await client.GetStringAsync("http://msdn.microsoft.com")

  • C#: string urlContents = await client.GetStringAsync("http://msdn.microsoft.com");

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.

Hh873191.collapse_all(pt-br,VS.110).gifEtapa 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 quatro

Hh873191.collapse_all(pt-br,VS.110).gifEtapa 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.

Etapa cinco

AccessTheWebAsync executa a conclusão, e o controle retorna a startButton_Click, que está aguardando a conclusão.

Hh873191.collapse_all(pt-br,VS.110).gifEtapa 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.

Etapa seis

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)