Tratando a reentrada em aplicativos assíncronos (C# e Visual Basic)

Ao incluir o código assíncrono em seu aplicativo, você deve considerar e possivelmente evitar a reentrada, que referencia a reentrada de uma operação assíncrona antes de terminar. Se você não identificar ou tratar possibilidades para reentrância, poderá causar resultados inesperados.

Neste tópico

Dica

As instruções em Examinando e executando o aplicativo de exemplo mostram como executar o código como um aplicativo Windows Presentation Foundation (WPF) ou como um aplicativo da Windows Store.

Para executar o exemplo como um aplicativo WPF, você deve ter o Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 para Windows Desktop, Visual Studio Express 2013 para Windows, ou o .NET Framework 4.5 ou 4.5.1 instalado em seu computador.

Como executar o exemplo como um aplicativo Windows Store, você deve ter o Windows 8 instalado em seu computador.Além disso, se quiser executar o exemplo no Visual Studio, você também deverá ter o Visual Studio 2012, o Visual Studio 2013, Visual Studio Express 2012 para Windows Desktop, ou Visual Studio Express 2013 para Windows instalado.

Reconhecendo Reentrada

No exemplo neste tópico, os usuários escolhem um botão Iniciar para iniciar um aplicativo assíncrono que baixa uma série de sites e calcula o número total de bytes baixados. Uma versão síncrona do exemplo poderia responder da mesma maneira independentemente de quantas vezes um usuário clicar no botão porque, após a primeira vez, o encadeamento da interface do usuário ignora os eventos até que o aplicativo termine de executar. Em um aplicativo assíncrono, no entanto, o thread de interface do usuário continua a responder e você pode reinserir a operação assíncrona antes de sua conclusão.

O exemplo a seguir mostra a saída esperada se o usuário escolhe o botão Iniciar apenas uma vez. Uma lista de sites baixados aparece com o tamanho, em bytes, de cada site. O número total de bytes aparece no final.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

No entanto, se o usuário escolher o botão mais de uma vez, o manipulador de eventos será chamado repetidamente e ele entrará no processo de download a cada vez. Como resultado, várias operações assíncronas são executadas ao mesmo tempo, a saída intercala os resultados, e o número de bytes total é confuso.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
7. msdn.microsoft.com                                            42972
4. msdn.microsoft.com/library/hh290140.aspx               117152
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

5. msdn.microsoft.com/library/hh524395.aspx                68959
1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
6. msdn.microsoft.com/library/ms404677.aspx               197325
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
7. msdn.microsoft.com                                            42972
5. msdn.microsoft.com/library/hh524395.aspx                68959
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Você pode examinar o código que gerencia essas saída rolando até o final deste tópico. Você pode fazer experiências com o código baixando a solução para seu computador local, e então executar o projeto de WebsiteDownload ou usar o código no final deste tópico para criar seu próprio projeto para mais informações e instruções, consulte Examinando e executando o aplicativo de exemplo.

Tratando Reentrada

Você pode manipular a reentrada em uma variedade de maneiras, dependendo do que você deseja que seu aplicativo faça. Este tópico apresenta os exemplos seguintes:

  • Desabilitar o Botão Iniciar

    Desabilite o botão Iniciar enquanto a operação estiver em execução de forma que o usuário não possa interrompê-la.

  • Cancelar e Reiniciar a Operação

    Cancele qualquer operação que ainda estejam em execução quando o usuário escolher o botão Iniciar novamente e permita que a operação solicitada mais recentemente continue.

  • Executar Várias Operações e Enfileirar a Saída

    Permite que todas as operações solicitadas sejam executadas de forma assíncrona, mas coordena a exibição de saída de modo que os resultados de cada operação apareçam juntos e na ordem.

Desabilitar o Botão Iniciar

Você pode bloquear o botão de Iniciar quando uma operação estiver sendo executada, desativando o botão na parte superior do manipulador de eventos StartButton_Click . Você pode então reativar o botão de dentro de um bloco de finally quando concluir a operação de modo que os usuários possam executar o aplicativo novamente.

O código a seguir mostra estas alterações, que são marcadas com asteriscos. Você pode adicionar as alterações no código no final deste tópico, ou pode fazer download do aplicativo concluído de Async Exemplos: Reentrada em aplicativos .NET Área De Trabalho ou Async Exemplos: Reentrada em aplicativos do Windows Store. O nome do projeto é DisableStartButton.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' This line is commented out to make the results clearer in the output.
    'ResultsTextBox.Text = ""

    ' ***Disable the Start button until the downloads are complete. 
    StartButton.IsEnabled = False

    Try
        Await AccessTheWebAsync()

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    ' ***Enable the Start button in case you want to run the program again. 
    Finally
        StartButton.IsEnabled = True

    End Try
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // This line is commented out to make the results clearer in the output.
    //ResultsTextBox.Text = "";

    // ***Disable the Start button until the downloads are complete. 
    StartButton.IsEnabled = false; 

    try
    {
        await AccessTheWebAsync();
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.";
    }
    // ***Enable the Start button in case you want to run the program again. 
    finally
    {
        StartButton.IsEnabled = true;
    }
}

Como resultado das alterações, o botão não responde enquanto AccessTheWebAsync estiver baixando sites, portanto, o processo não pode ser iniciado novamente.

Cancelar e Reiniciar a Operação

Em vez de desabilitar o botão Iniciar, você poderá manter o botão ativo mas, se o usuário escolher o botão novamente, cancelará a operação que já estiver em execução e permitirá que a operação iniciada mais recentemente continue.

Para obter mais informações sobre cancelamento, consulte Ajustando seu aplicativo do Async.

Para configurar esse cenário, faça as seguintes alterações no código básico fornecido em Examinar e executar o aplicativo de exemplo. Você também pode fazer download do arquivo finalizado de Exemplos do Async: reentrada em aplicativos .NET da área de trabalho ou Exemplos do Async: reentrada em aplicativos da Windows Store. O nome desse projeto é CancelAndRestart.

  1. Declarar uma variável CancellationTokenSource, cts, no escopo para todos os métodos.

    Class MainWindow // Or Class MainPage
    
        ' *** Declare a System.Threading.CancellationTokenSource.
        Dim cts As CancellationTokenSource
    
    public partial class MainWindow : Window   // Or class MainPage
    {
        // *** Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;
    
  2. Em StartButton_Click, determine se uma operação já está em andamento. Se o valor de cts for nulo (Nothing no Visual Basic), nenhuma operação já estará ativa. Se o valor não for nulo, a operação que já estiver em execução será cancelada.

    ' *** If a download process is already underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If
    
    // *** If a download process is already underway, cancel it.
    if (cts != null)
    {
        cts.Cancel();
    }
    
  3. Definir cts para um valor diferente que representa o processo atual.

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS
    
    // *** Now set cts to a new value that you can use to cancel the current process
    // if the button is chosen again.
    CancellationTokenSource newCTS = new CancellationTokenSource();
    cts = newCTS;
    
  4. No final de StartButton_Click, o processo atual estará completo, então defina o valor de cts de volta para zero.

    ' *** When the process completes, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
    
    // *** When the process is complete, signal that another process can begin.
    if (cts == newCTS)
        cts = null;
    

O código a seguir mostra todas as alterações em StartButton_Click. As adições são marcadas com asteriscos.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)

    ' This line is commented out to make the results clearer. 
    'ResultsTextBox.Text = ""


    ' *** If a download process is underway, cancel it.
    If cts IsNot Nothing Then
        cts.Cancel()
    End If

    ' *** Now set cts to cancel the current process if the button is chosen again.
    Dim newCTS As CancellationTokenSource = New CancellationTokenSource()
    cts = newCTS

    Try
        ' *** Send a token to carry the message if the operation is canceled.
        Await AccessTheWebAsync(cts.Token)
        

    Catch ex As OperationCanceledException
        ResultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed."
    End Try

    ' *** When the process is complete, signal that another process can proceed.
    If cts Is newCTS Then
        cts = Nothing
    End If
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // This line is commented out to make the results clearer in the output.
    //ResultsTextBox.Clear();

    // *** If a download process is already underway, cancel it.
    if (cts != null)
    {
        cts.Cancel();
    }

    // *** Now set cts to cancel the current process if the button is chosen again.
    CancellationTokenSource newCTS = new CancellationTokenSource();
    cts = newCTS;

    try
    {
        // ***Send cts.Token to carry the message if there is a cancellation request.
        await AccessTheWebAsync(cts.Token);
        
    }
    // *** Catch cancellations separately.
    catch (OperationCanceledException)
    {
        ResultsTextBox.Text += "\r\nDownloads canceled.\r\n";
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.\r\n";
    }
    // *** When the process is complete, signal that another process can proceed.
    if (cts == newCTS)
        cts = null;
}

No AccessTheWebAsync, faça as seguintes alterações.

  • Adicionar um parâmetro para aceitar o símbolo de cancelamento de StartButton_Click.

  • Use o método GetAsync para fazer download de sites porque GetAsync aceita um argumento CancellationToken.

  • Antes de chamar DisplayResults para exibir os resultados para cada site baixado, verifique ct para certificar-se de que a operação atual não seja cancelada.

O código a seguir mostra estas alterações, que são marcadas com asteriscos.

' *** Provide a parameter for the CancellationToken from StartButton_Click.
Private Async Function AccessTheWebAsync(ct As CancellationToken) As Task

    ' Declare an HttpClient object.
    Dim client = New HttpClient()

    ' Make a list of web addresses.
    Dim urlList As List(Of String) = SetUpURLList()

    Dim total = 0
    Dim position = 0

    For Each url In urlList
        ' *** Use the HttpClient.GetAsync method because it accepts a 
        ' cancellation token.
        Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)

        ' *** Retrieve the website contents from the HttpResponseMessage.
        Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()

        ' *** Check for cancellations before displaying information about the 
        ' latest site. 
        ct.ThrowIfCancellationRequested()

        position += 1
        DisplayResults(url, urlContents, position)

        ' Update the total.
        total += urlContents.Length
    Next

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function
// *** Provide a parameter for the CancellationToken from StartButton_Click.
async Task AccessTheWebAsync(CancellationToken ct)
{
    // Declare an HttpClient object.
    HttpClient client = new HttpClient();

    // Make a list of web addresses.
    List<string> urlList = SetUpURLList();

    var total = 0;
    var position = 0;

    foreach (var url in urlList)
    {
        // *** Use the HttpClient.GetAsync method because it accepts a 
        // cancellation token.
        HttpResponseMessage response = await client.GetAsync(url, ct);

        // *** Retrieve the website contents from the HttpResponseMessage.
        byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

        // *** Check for cancellations before displaying information about the 
        // latest site. 
        ct.ThrowIfCancellationRequested();

        DisplayResults(url, urlContents, ++position);

        // Update the total.
        total += urlContents.Length;
    }

    // Display the total count for all of the websites.
    ResultsTextBox.Text +=
        string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
}   

Se você escolher o botão Iniciar várias vezes enquanto este aplicativo estiver em execução, ele deverá produzir resultados parecidos com a saída a seguir.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               122505
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
Download canceled.

1. msdn.microsoft.com/library/hh191443.aspx                83732
2. msdn.microsoft.com/library/aa578028.aspx               205273
3. msdn.microsoft.com/library/jj155761.aspx                29019
4. msdn.microsoft.com/library/hh290140.aspx               117152
5. msdn.microsoft.com/library/hh524395.aspx                68959
6. msdn.microsoft.com/library/ms404677.aspx               197325
7. msdn.microsoft.com                                            42972
8. msdn.microsoft.com/library/ff730837.aspx               146159

TOTAL bytes returned:  890591

Para eliminar as listas parciais, tire os comentários da primeira linha de código em StartButton_Click para desmarcar a caixa de texto sempre que o usuário reiniciar a operação.

Executar Várias Operações e Enfileirar a Saída

Este terceiro exemplo é o mais complicado, no qual o aplicativo é iniciado com outra operação assíncrona sempre que o usuário escolhe o botão Iniciar, e todas as operações são executadas até a conclusão. Todas as operações solicitadas baixam sites da lista de forma assíncrona, mas a saída das operações é apresentada em sequência. Ou seja, a atividade real de download é intercalada, pois a saída em Reconhecendo Reentrada é exibida, mas a lista de resultados para cada grupo é apresentada separadamente.

As operações compartilham uma Task global, pendingWork, que serve como um gatekeeper para o processo de exibição.

Você pode executar este exemplo colando as alterações no código em Compilar o aplicativo, ou pode seguir as instruções em Download do aplicativo para fazer download do exemplo e então executar o projeto de QueueResults.

A saída a seguir mostra o resultado se o usuário escolhe o botão Iniciar apenas uma vez. O rótulo da letra, A, indica que o resultado é referente à primeira vez que o botão Iniciar é escolhido. Os números mostram a ordem das URLs na lista de destinos de download.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               209858
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71260
A-6. msdn.microsoft.com/library/ms404677.aspx               199186
A-7. msdn.microsoft.com                                            53266
A-8. msdn.microsoft.com/library/ff730837.aspx               148020

TOTAL bytes returned:  918876


#Group A is complete.

Se o usuário escolher o botão Iniciar três vezes, o aplicativo produzirá saída parecida com as linhas a seguir. As linhas de informações que começam com um sustenido (#) acompanham o andamento do aplicativo.

#Starting group A.
#Task assigned for group A.

A-1. msdn.microsoft.com/library/hh191443.aspx                87389
A-2. msdn.microsoft.com/library/aa578028.aspx               207089
A-3. msdn.microsoft.com/library/jj155761.aspx                30870
A-4. msdn.microsoft.com/library/hh290140.aspx               119027
A-5. msdn.microsoft.com/library/hh524395.aspx                71259
A-6. msdn.microsoft.com/library/ms404677.aspx               199185

#Starting group B.
#Task assigned for group B.

A-7. msdn.microsoft.com                                            53266

#Starting group C.
#Task assigned for group C.

A-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916095

B-1. msdn.microsoft.com/library/hh191443.aspx                87389
B-2. msdn.microsoft.com/library/aa578028.aspx               207089
B-3. msdn.microsoft.com/library/jj155761.aspx                30870
B-4. msdn.microsoft.com/library/hh290140.aspx               119027
B-5. msdn.microsoft.com/library/hh524395.aspx                71260
B-6. msdn.microsoft.com/library/ms404677.aspx               199186

#Group A is complete.

B-7. msdn.microsoft.com                                            53266
B-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  916097

C-1. msdn.microsoft.com/library/hh191443.aspx                87389
C-2. msdn.microsoft.com/library/aa578028.aspx               207089

#Group B is complete.

C-3. msdn.microsoft.com/library/jj155761.aspx                30870
C-4. msdn.microsoft.com/library/hh290140.aspx               119027
C-5. msdn.microsoft.com/library/hh524395.aspx                72765
C-6. msdn.microsoft.com/library/ms404677.aspx               199186
C-7. msdn.microsoft.com                                            56190
C-8. msdn.microsoft.com/library/ff730837.aspx               148010

TOTAL bytes returned:  920526

#Group C is complete.

Os grupos B e C começam antes de o grupo A ter terminado, mas o resultado de cada grupo aparece separadamente. Todas as saídas do grupo A aparecem primeiro, seguidas por todas as saídas do grupo B e, em seguida, todas as saídas do grupo C. O aplicativo sempre exibe os grupos na ordem e, para cada grupo, sempre exibe as informações sobre os sites individuais na ordem em que os URLs aparecem na lista de URLs.

No entanto, você não pode prever a ordem em que os downloads realmente acontecerão. Após vários grupos serem iniciados, todas as tarefas de download que eles gerenciam ficam ativas. Você não pode assumir que A-1 será antes de B-1, e você não pode assumir que A-1 será baixado antes de A-2.

Definições Globais

O exemplo de código contém as duas declarações globais que são visíveis de todos os métodos.

Class MainWindow    ' Class MainPage in Windows Store app.

    ' ***Declare the following variables where all methods can access them.  
    Private pendingWork As Task = Nothing 
    Private group As Char = ChrW(AscW("A") - 1)
public partial class MainWindow : Window  // Class MainPage in Windows Store app.
{
    // ***Declare the following variables where all methods can access them.  
    private Task pendingWork = null;   
    private char group = (char)('A' - 1);

A Task variável pendingWork vigia o processo de exibição e impede qualquer grupo de interromper a operação de exibição de outro grupo. A variável de caracteres, group, rotula a saída de grupos diferentes para verificar se os resultados aparecem na ordem esperada.

O manipulador de evento de clique

O manipulador de eventos, StartButton_Click, incrementa a letra do grupo sempre que o usuário escolhe o botão Iniciar. O manipulador chama AccessTheWebAsync para executar a operação de transferência.

Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
    ' ***Verify that each group's results are displayed together, and that 
    ' the groups display in order, by marking each group with a letter.
    group = ChrW(AscW(group) + 1)
    ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Starting group {0}.", group)

    Try 
        ' *** Pass the group value to AccessTheWebAsync. 
        Dim finishedGroup As Char = Await AccessTheWebAsync(group)

        ' The following line verifies a successful return from the download and  
        ' display procedures. 
        ResultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "#Group {0} is complete." & vbCrLf, finishedGroup)

    Catch ex As Exception
        ResultsTextBox.Text &= vbCrLf & "Downloads failed." 

    End Try 
End Sub
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // ***Verify that each group's results are displayed together, and that 
    // the groups display in order, by marking each group with a letter. 
    group = (char)(group + 1);
    ResultsTextBox.Text += string.Format("\r\n\r\n#Starting group {0}.", group);

    try
    {
        // *** Pass the group value to AccessTheWebAsync. 
        char finishedGroup = await AccessTheWebAsync(group);

        // The following line verifies a successful return from the download and 
        // display procedures. 
        ResultsTextBox.Text += string.Format("\r\n\r\n#Group {0} is complete.\r\n", finishedGroup);
    }
    catch (Exception)
    {
        ResultsTextBox.Text += "\r\nDownloads failed.";
    }
}

O método AccessTheWebAsync

Este exemplo divide AccessTheWebAsync em dois métodos. O primeiro método, AccessTheWebAsync, inicia todas as tarefas de download para um grupo e configura pendingWork para controlar o processo de exibição. O método usa uma consulta LINQ e ToArray``1 para iniciar todas as tarefas de download ao mesmo tempo.

AccessTheWebAsync então chama FinishOneGroupAsync para aguardar conclusão de cada download e exibir seu comprimento.

FinishOneGroupAsync retorna uma tarefa atribuída a pendingWork em AccessTheWebAsync. O valor que impede a interrupção por outra operação antes que a tarefa seja concluída.

Private Async Function AccessTheWebAsync(grp As Char) As Task(Of Char)

    Dim client = New HttpClient()

    ' Make a list of the web addresses to download. 
    Dim urlList As List(Of String) = SetUpURLList()

    ' ***Kick off the downloads. The application of ToArray activates all the download tasks. 
    Dim getContentTasks As Task(Of Byte())() =
        urlList.Select(Function(addr) client.GetByteArrayAsync(addr)).ToArray()

    ' ***Call the method that awaits the downloads and displays the results. 
    ' Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp)

    ResultsTextBox.Text &=
        String.Format(vbCrLf & "#Task assigned for group {0}. Download tasks are active." & vbCrLf, grp)

    ' ***This task is complete when a group has finished downloading and displaying.
    Await pendingWork

    ' You can do other work here or just return. 
    Return grp
End Function
private async Task<char> AccessTheWebAsync(char grp)
{
    HttpClient client = new HttpClient();

    // Make a list of the web addresses to download.
    List<string> urlList = SetUpURLList();

    // ***Kick off the downloads. The application of ToArray activates all the download tasks.
    Task<byte[]>[] getContentTasks = urlList.Select(url => client.GetByteArrayAsync(url)).ToArray();

    // ***Call the method that awaits the downloads and displays the results. 
    // Assign the Task that FinishOneGroupAsync returns to the gatekeeper task, pendingWork.
    pendingWork = FinishOneGroupAsync(urlList, getContentTasks, grp);

    ResultsTextBox.Text += string.Format("\r\n#Task assigned for group {0}. Download tasks are active.\r\n", grp);

    // ***This task is complete when a group has finished downloading and displaying.
    await pendingWork;

    // You can do other work here or just return. 
    return grp;
}

O método FinishOneGroupAsync

Este método percorre as tarefas de download em um grupo, aguardando cada uma, exibindo o tamanho do site baixado e adicionando o tamanho ao total.

A primeira instrução em FinishOneGroupAsync usa pendingWork para verificar se o método inserido não interfere em uma operação que já está no processo de exibição ou que já está aguardando. Se tal operação estiver em andamento, a operação de inserção deverá aguardar sua vez.

Private Async Function FinishOneGroupAsync(urls As List(Of String), contentTasks As Task(Of Byte())(), grp As Char) As Task

    ' Wait for the previous group to finish displaying results. 
    If pendingWork IsNot Nothing Then
        Await pendingWork
    End If 

    Dim total = 0

    ' contentTasks is the array of Tasks that was created in AccessTheWebAsync. 
    For i As Integer = 0 To contentTasks.Length - 1
        ' Await the download of a particular URL, and then display the URL and 
        ' its length. 
        Dim content As Byte() = Await contentTasks(i)
        DisplayResults(urls(i), content, i, grp)
        total += content.Length
    Next 

    ' Display the total count for all of the websites.
    ResultsTextBox.Text &=
        String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
End Function
private async Task FinishOneGroupAsync(List<string> urls, Task<byte[]>[] contentTasks, char grp)
{
    // ***Wait for the previous group to finish displaying results. 
    if (pendingWork != null) await pendingWork;

    int total = 0;

    // contentTasks is the array of Tasks that was created in AccessTheWebAsync. 
    for (int i = 0; i < contentTasks.Length; i++)
    {
        // Await the download of a particular URL, and then display the URL and 
        // its length. 
        byte[] content = await contentTasks[i];
        DisplayResults(urls[i], content, i, grp);
        total += content.Length;
    }

    // Display the total count for all of the websites.
    ResultsTextBox.Text +=
        string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
}

Você pode executar este exemplo colando as alterações no código em Compilar o aplicativo, ou pode seguir as instruções em Download do aplicativo para fazer download do exemplo, e então executar o projeto de QueueResults.

Pontos de Interesse

As linhas de informações que começam com um sustenido (#) na saída esclarecem como este exemplo funciona.

A saída mostra os seguintes padrões:

  • Um grupo pode ser iniciado quando um grupo anterior exibir sua saída, mas a exibição da saída do grupo anterior não é interrompida.

    #Starting group A.
    #Task assigned for group A. Download tasks are active.
    
    A-1. msdn.microsoft.com/library/hh191443.aspx                87389
    A-2. msdn.microsoft.com/library/aa578028.aspx               207089
    A-3. msdn.microsoft.com/library/jj155761.aspx                30870
    A-4. msdn.microsoft.com/library/hh290140.aspx               119037
    A-5. msdn.microsoft.com/library/hh524395.aspx                71260
    
    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    
    A-6. msdn.microsoft.com/library/ms404677.aspx               199186
    A-7. msdn.microsoft.com                                            53078
    A-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915919
    
    B-1. msdn.microsoft.com/library/hh191443.aspx                87388
    B-2. msdn.microsoft.com/library/aa578028.aspx               207089
    B-3. msdn.microsoft.com/library/jj155761.aspx                30870
    
    #Group A is complete.
    
    B-4. msdn.microsoft.com/library/hh290140.aspx               119027
    B-5. msdn.microsoft.com/library/hh524395.aspx                71260
    B-6. msdn.microsoft.com/library/ms404677.aspx               199186
    B-7. msdn.microsoft.com                                            53078
    B-8. msdn.microsoft.com/library/ff730837.aspx               148010
    
    TOTAL bytes returned:  915908
    
  • A tarefa pendingWork é nula (Nothing no Visual Basic) no início de FinishOneGroupAsync somente para o grupo A, que iniciou primeiro. O grupo A ainda não concluiu uma expressão await ao atingir FinishOneGroupAsync. Portanto, o controle não retornou para AccessTheWebAsync e a primeira atribuição para pendingWork não ocorreu.

  • As duas linhas sempre aparecem juntas na saída. O código nunca é interrompido entre a inicialização da operação de um grupo em StartButton_Click e a atribuição de uma tarefa ao grupo para pendingWork.

    #Starting group B.
    #Task assigned for group B. Download tasks are active.
    

    Após um grupo entrar em StartButton_Click, a operação não conclui uma expressão de espera até que a operação entre em FinishOneGroupAsync. Portanto, nenhuma outra operação pode ganhar controle durante esse segmento de código.

Examinando e executando o aplicativo de exemplo

Para compreender melhor o aplicativo de exemplo, você poderá baixá-lo, compilá-lo você mesmo ou examinar o código no final deste tópico sem implementar o aplicativo.

Dica

Para executar o exemplo como um aplicativo de área de trabalho do Windows Presentation Foundation (WPF), você deve ter o Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 para Windows Desktop, Visual Studio Express 2013 para Windows, ou o .NET Framework 4.5 ou 4.5.1 instalado no seu computador.

Como executar o exemplo como um aplicativo Windows Store, você deve ter o Windows 8 instalado em seu computador.Além disso, se quiser executar o exemplo no Visual Studio, você também deverá ter o Visual Studio 2012, o Visual Studio 2013, Visual Studio Express 2012 para Windows 8 ou o Visual Studio Express 2013 para Windows instalado.O Visual Studio 2010 não pode carregar os projetos que são destinados para o .NET Framework 4.5.

Baixando o aplicativo

  1. Baixe o arquivo compactado de Exemplos do Async: reentrada em aplicativos .NET da área de trabalho ou Exemplos do Async: reentrada em aplicativos da Windows Store.

  2. Descompacte o arquivo que você baixou e inicie o Visual Studio.

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

  4. Navegue até a pasta que contém o código de exemplo descompactado, e abra o arquivo de solução (.sln).

  5. No Gerenciador de Soluções, abra o menu de atalho para o projeto que você deseja executar e, em seguida, selecione Definir como Projeto de Inicialização.

  6. Use as teclas CTRL + F5 para compilar e executar o projeto.

Compilando o aplicativo

As seções a seguir fornecem o código para compilar o exemplo como um aplicativo do WPF ou como um aplicativo do Windows Store.

Para compilar um aplicativo WPF

  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 Modelos Instalados, expanda Visual Basic ou Visual C# e então expanda Windows.

  4. Na lista de tipos de projeto, selecione Aplicativo WPF.

  5. Nomeie o projeto WebsiteDownloadWPF e clique no botão OK.

    O novo projeto aparece no Gerenciador de Soluções.

  6. No Editor de Códigos do Visual Studio, escolha a guia MainWindow.xaml.

    Se a guia não está visível, abra o menu de atalho de MainWindow.xaml no Gerenciador de Soluções e escolha Exibir código.

  7. No modo de exibição XAML de MainWindow.xaml, substitua o código pelo seguinte.

    <Window x:Class="MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    
    <Window x:Class="WebsiteDownloadWPF.MainWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWPF"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d">
    
        <Grid Width="517" Height="360">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="-1,0,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="53" Background="#FFA89B9B" FontSize="36" Width="518"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="-1,53,0,-36" TextWrapping="Wrap" VerticalAlignment="Top" Height="343" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="518" FontFamily="Lucida Console" />
        </Grid>
    </Window>
    

    Uma janela simples que contém uma caixa de texto e um botão aparece no modo de exibição Design de MainWindow.xaml.

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

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

  10. Em MainWindow.xaml.vb ou MainWindow.xaml.cs, substitua o código com o código a seguir.

    ' Add the following Imports statements, and add a reference for System.Net.Http. 
    Imports System.Net.Http
    Imports System.Threading
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ' This line is commented out to make the results clearer in the output. 
            'ResultsTextBox.Text = "" 
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed." 
    
            End Try 
        End Sub 
    
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object. 
            Dim client = New HttpClient()
    
            ' Make a list of web addresses. 
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task 
                ' produces a byte array. 
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next 
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function 
    
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
            Return urls
        End Function 
    
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed 
            ' to be used with a monospaced font, such as Lucida Console or 
            ' Global Monospace. 
    
            ' Strip off the "http:'". 
            Dim displayURL = url.Replace("http://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub 
    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 the following using directives, and add a reference for System.Net.Http. 
    using System.Net.Http;
    using System.Threading;
    
    namespace WebsiteDownloadWPF
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                // This line is commented out to make the results clearer in the output. 
                //ResultsTextBox.Text = "";
    
                try
                {
                    await AccessTheWebAsync();
                }
                catch (Exception)
                {
                    ResultsTextBox.Text += "\r\nDownloads failed.";
                }
            }
    
    
            private async Task AccessTheWebAsync()
            {
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                // Make a list of web addresses.
                List<string> urlList = SetUpURLList();
    
                var total = 0;
                var position = 0;
    
                foreach (var url in urlList)
                {
                    // GetByteArrayAsync returns a task. At completion, the task 
                    // produces a byte array. 
                    byte[] urlContents = await client.GetByteArrayAsync(url);
    
                    DisplayResults(url, urlContents, ++position);
    
                    // Update the total.
                    total += urlContents.Length;
                }
    
                // Display the total count for all of the websites.
                ResultsTextBox.Text +=
                    string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
            }
    
    
            private List<string> SetUpURLList()
            {
                List<string> urls = new List<string> 
                { 
                    "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                    "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                    "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "https://msdn.microsoft.com",
                    "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
    
    
            private void DisplayResults(string url, byte[] content, int pos)
            {
                // Display the length of each website. The string format is designed 
                // to be used with a monospaced font, such as Lucida Console or  
                // Global Monospace. 
    
                // Strip off the "http://".
                var displayURL = url.Replace("http://", "");
                // Display position in the URL list, the URL, and the number of bytes.
                ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length);
            }
        }
    }
    
  11. Use as teclas CTRL+F5 para executar o programa e, em seguida, clique no botão Iniciar várias vezes.

  12. Faça as alterações de Desabilitar o botão Iniciar, de Cancelar e reiniciar a operação ou de Executar várias operações e enfileirar a saída para manipular o reentrada.

Para criar um aplicativo da Windows Store

  1. Inicie o Visual Studio.

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

    A Caixa de diálogo Novo Projeto é exibida.

  3. Na categoria Instalado, Modelos, expanda Visual Basic ou Visual C# e, em seguida, expanda Windows Store.

  4. Na lista de tipos de projeto, selecione Aplicativo em Branco (XAML).

  5. Nomeie o projeto WebsiteDownloadWin e clique no botão OK.

    O novo projeto aparece no Gerenciador de Soluções.

  6. Em Gerenciador de Soluções, abra o menu de atalho para MainPage.xaml e escolha Abrir.

  7. Na janela XAML de MainPage.xaml, substitua o código pelo seguinte.

    <Page
        x:Class="WebsiteDownloadWin.MainPage"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:WebsiteDownloadWin"
        xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" FontSize="12">
    
        <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="325,77,0,0" VerticalAlignment="Top" Click="StartButton_Click" Height="145" Background="#FFA89B9B" FontSize="36" Width="711"  />
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" Margin="325,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="546" FontSize="10" ScrollViewer.VerticalScrollBarVisibility="Visible" Width="711" FontFamily="Lucida Console" />
        </Grid>
    </Page>
    

    Uma janela simples que contém uma caixa de texto e um botão Início aparecem na janela Design do MainPage.xaml.

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

  9. Substitua o código em MainPage.xaml.vb ou em MainPage.xaml.cs pelo código a seguir.

    ' Add the following Imports statements. 
    Imports System.Threading.Tasks
    Imports System.Threading
    Imports System.Net.Http
    
    Public NotInheritable Class MainPage
        Inherits Page
    
        Protected Overrides Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        End Sub 
    
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs)
            ' This line is commented out to make the results clearer in the output. 
            'ResultsTextBox.Text = "" 
    
            Try
                Await AccessTheWebAsync()
    
            Catch ex As Exception
                ResultsTextBox.Text &= vbCrLf & "Downloads failed." 
    
            End Try 
        End Sub 
    
    
        Private Async Function AccessTheWebAsync() As Task
    
            ' Declare an HttpClient object. 
            Dim client = New HttpClient()
    
            ' Make a list of web addresses. 
            Dim urlList As List(Of String) = SetUpURLList()
    
            Dim total = 0
            Dim position = 0
    
            For Each url In urlList
                ' GetByteArrayAsync returns a task. At completion, the task 
                ' produces a byte array. 
                Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
                position += 1
                DisplayResults(url, urlContents, position)
    
                ' Update the total.
                total += urlContents.Length
            Next 
    
            ' Display the total count for all of the websites.
            ResultsTextBox.Text &=
                String.Format(vbCrLf & vbCrLf & "TOTAL bytes returned:  " & total & vbCrLf)
        End Function 
    
    
        Private Function SetUpURLList() As List(Of String)
            Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
            Return urls
        End Function 
    
    
        Private Sub DisplayResults(url As String, content As Byte(), pos As Integer)
            ' Display the length of each website. The string format is designed 
            ' to be used with a monospaced font, such as Lucida Console or 
            ' Global Monospace. 
    
            ' Strip off the "http:'". 
            Dim displayURL = url.Replace("http://", "")
            ' Display position in the URL list, the URL, and the number of bytes.
            ResultsTextBox.Text &= String.Format(vbCrLf & "{0}. {1,-58} {2,8}", pos, displayURL, content.Length)
        End Sub 
    End Class
    
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using Windows.Foundation;
    using Windows.Foundation.Collections;
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    using Windows.UI.Xaml.Controls.Primitives;
    using Windows.UI.Xaml.Data;
    using Windows.UI.Xaml.Input;
    using Windows.UI.Xaml.Media;
    using Windows.UI.Xaml.Navigation;
    
    // Add the following using directives.  
    using System.Threading.Tasks;
    using System.Threading;
    using System.Net.Http;
    
    
    namespace WebsiteDownloadWin
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
            private async void StartButton_Click(object sender, RoutedEventArgs e)
            {
                // This line is commented out to make the results clearer in the output. 
                //ResultsTextBox.Text = "";
    
                try
                {
                    await AccessTheWebAsync();
                }
                catch (Exception)
                {
                    ResultsTextBox.Text += "\r\nDownloads failed.";
                }
            }
    
    
            private async Task AccessTheWebAsync()
            {
                // Declare an HttpClient object.
                HttpClient client = new HttpClient();
    
                // Make a list of web addresses.
                List<string> urlList = SetUpURLList();
    
                var total = 0;
                var position = 0;
    
                foreach (var url in urlList)
                {
                    // GetByteArrayAsync returns a task. At completion, the task 
                    // produces a byte array. 
                    byte[] urlContents = await client.GetByteArrayAsync(url);
    
                    DisplayResults(url, urlContents, ++position);
    
                    // Update the total.
                    total += urlContents.Length;
                }
    
                // Display the total count for all of the websites.
                ResultsTextBox.Text +=
                    string.Format("\r\n\r\nTOTAL bytes returned:  {0}\r\n", total);
            }
    
    
            private List<string> SetUpURLList()
            {
                List<string> urls = new List<string> 
                { 
                    "https://msdn.microsoft.com/en-us/library/hh191443.aspx",
                    "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                    "https://msdn.microsoft.com/en-us/library/jj155761.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                    "https://msdn.microsoft.com/en-us/library/hh524395.aspx",
                    "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                    "https://msdn.microsoft.com",
                    "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
                };
                return urls;
            }
    
    
            private void DisplayResults(string url, byte[] content, int pos)
            {
                // Display the length of each website. The string format is designed 
                // to be used with a monospaced font, such as Lucida Console or 
                // Global Monospace. 
    
                // Strip off the "http://".
                var displayURL = url.Replace("http://", "");
                // Display position in the URL list, the URL, and the number of bytes.
                ResultsTextBox.Text += string.Format("\n{0}. {1,-58} {2,8}", pos, displayURL, content.Length);
            }
        }
    }
    
  10. Use as teclas CTRL+F5 para executar o programa e, em seguida, clique no botão Iniciar várias vezes.

  11. Faça as alterações de Desabilitar o botão Iniciar, de Cancelar e reiniciar a operação ou de Executar várias operações e enfileirar a saída para manipular o reentrada.

Consulte também

Tarefas

Instruções passo a passo: acessando a Web e usando Async e Await (C# e Visual Basic)

Conceitos

Programação assíncrona com Async e Await (C# e Visual Basic)

Outros recursos

Programação assíncrona (aplicativos da Windows Store)

Quickstart: Chamando APIs assíncronos em C# ou Visual Basic