Iniciar várias tarefas assíncronas e processá-las à medida que forem concluídas (Visual Basic)
Task.WhenAnyUsando o , você pode iniciar várias tarefas ao mesmo tempo e processá-las uma a uma à medida que são concluídas, em vez de processá-las na ordem em que são iniciadas.
O exemplo a seguir usa uma consulta para criar uma coleção de tarefas. Cada tarefa baixa o conteúdo de um site especificado. Em cada iteração de um loop while, uma chamada aguardada para WhenAny
retornar a tarefa na coleção de tarefas que termina seu download primeiro. Essa tarefa é removida da coleção e processada. O loop se repete até que a coleção não contenha mais tarefas.
Nota
Para executar os exemplos, você deve ter o Visual Studio 2012 ou mais recente e o .NET Framework 4.5 ou mais recente instalado no seu computador.
Download do exemplo
Você pode baixar o projeto completo do Windows Presentation Foundation (WPF) de Async Sample: Fine Tuning Your Application e siga estas etapas.
Descompacte o arquivo que você baixou e, em seguida, inicie o Visual Studio.
Na barra de menus, escolha Arquivo, Abrir, Projeto/Solução.
Na caixa de diálogo Abrir projeto, abra a pasta que contém o código de exemplo que você descompactou e, em seguida, abra o arquivo de solução (.sln) para AsyncFineTuningVB.
No Gerenciador de Soluções, abra o menu de atalho para o projeto ProcessTasksAsTheyFinish e escolha Definir como Projeto de Inicialização.
Escolha a tecla F5 para executar o projeto.
Escolha as teclas Ctrl+F5 para executar o projeto sem depurá-lo.
Execute o projeto várias vezes para verificar se os comprimentos baixados nem sempre aparecem na mesma ordem.
Se não quiser baixar o projeto, você pode revisar o arquivo de MainWindow.xaml.vb no final deste tópico.
Construindo o exemplo
Este exemplo adiciona ao código desenvolvido em Cancelar tarefas assíncronas restantes após a conclusão de uma (Visual Basic) e usa a mesma interface do usuário.
Para criar o exemplo por conta própria, passo a passo, siga as instruções na seção "Baixando o exemplo", mas escolha CancelAfterOneTask como o projeto de inicialização. Adicione as alterações neste tópico ao AccessTheWebAsync
método nesse projeto. As alterações estão assinaladas com asteriscos.
O projeto CancelAfterOneTask já inclui uma consulta que, quando executada, cria uma coleção de tarefas. Cada chamada para ProcessURLAsync
no código a seguir retorna um Task<TResult> onde TResult
é um inteiro.
Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
From url In urlList Select ProcessURLAsync(url, client, ct)
No arquivo MainWindow.xaml.vb do projeto, faça as seguintes alterações no AccessTheWebAsync
método.
Execute a consulta aplicando Enumerable.ToList em vez de ToArray.
Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
Adicione um loop while que execute as seguintes etapas para cada tarefa na coleção.
Aguarda uma chamada para
WhenAny
identificar a primeira tarefa na coleção para concluir seu download.Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
Remove essa tarefa da coleção.
downloadTasks.Remove(finishedTask)
Aguarda
finishedTask
, que é retornado por uma chamada paraProcessURLAsync
. AfinishedTask
variável é um Task<TResult> ondeTReturn
é um inteiro. A tarefa já está concluída, mas você aguarda para recuperar o comprimento do site baixado, como mostra o exemplo a seguir.Dim length = Await finishedTask resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
Você deve executar o projeto várias vezes para verificar se os comprimentos baixados nem sempre aparecem na mesma ordem.
Atenção
Você pode usar WhenAny
em um loop, como descrito no exemplo, para resolver problemas que envolvem um pequeno número de tarefas. No entanto, outras abordagens são mais eficientes se você tiver um grande número de tarefas para processar. Para obter mais informações e exemplos, consulte Processando tarefas à medida que são concluídas.
Exemplo completo
O código a seguir é o texto completo do arquivo MainWindow.xaml.vb para o exemplo. Asteriscos marcam os elementos que foram adicionados para este exemplo.
Observe que você deve adicionar uma referência para System.Net.Http.
Você pode baixar o projeto de Async Sample: Fine Tuning Your Application.
' Add an Imports directive and a reference for System.Net.Http.
Imports System.Net.Http
' Add the following Imports directive for System.Threading.
Imports System.Threading
Class MainWindow
' Declare a System.Threading.CancellationTokenSource.
Dim cts As CancellationTokenSource
Private Async Sub startButton_Click(sender As Object, e As RoutedEventArgs)
' Instantiate the CancellationTokenSource.
cts = New CancellationTokenSource()
resultsTextBox.Clear()
Try
Await AccessTheWebAsync(cts.Token)
resultsTextBox.Text &= vbCrLf & "Downloads complete."
Catch ex As OperationCanceledException
resultsTextBox.Text &= vbCrLf & "Downloads canceled." & vbCrLf
Catch ex As Exception
resultsTextBox.Text &= vbCrLf & "Downloads failed." & vbCrLf
End Try
' Set the CancellationTokenSource to Nothing when the download is complete.
cts = Nothing
End Sub
' You can still include a Cancel button if you want to.
Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)
If cts IsNot Nothing Then
cts.Cancel()
End If
End Sub
' Provide a parameter for the CancellationToken.
' Change the return type to Task because the method has no return statement.
Async Function AccessTheWebAsync(ct As CancellationToken) As Task
Dim client As HttpClient = New HttpClient()
' Call SetUpURLList to make a list of web addresses.
Dim urlList As List(Of String) = SetUpURLList()
' ***Create a query that, when executed, returns a collection of tasks.
Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
From url In urlList Select ProcessURLAsync(url, client, ct)
' ***Use ToList to execute the query and start the download tasks.
Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
' ***Add a loop to process the tasks one at a time until none remain.
While downloadTasks.Count > 0
' ***Identify the first task that completes.
Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
' ***Remove the selected task from the list so that you don't
' process it more than once.
downloadTasks.Remove(finishedTask)
' ***Await the first completed task and display the results.
Dim length = Await finishedTask
resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
End While
End Function
' Bundle the processing steps for a website into one async method.
Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)
' GetAsync returns a Task(Of HttpResponseMessage).
Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)
' Retrieve the website contents from the HttpResponseMessage.
Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()
Return urlContents.Length
End Function
' Add a method that creates a list of web addresses.
Private Function SetUpURLList() As List(Of String)
Dim urls = New List(Of String) From
{
"https://msdn.microsoft.com",
"https://msdn.microsoft.com/library/hh290138.aspx",
"https://msdn.microsoft.com/library/hh290140.aspx",
"https://msdn.microsoft.com/library/dd470362.aspx",
"https://msdn.microsoft.com/library/aa578028.aspx",
"https://msdn.microsoft.com/library/ms404677.aspx",
"https://msdn.microsoft.com/library/ff730837.aspx"
}
Return urls
End Function
End Class
' Sample output:
' Length of the download: 226093
' Length of the download: 412588
' Length of the download: 175490
' Length of the download: 204890
' Length of the download: 158855
' Length of the download: 145790
' Length of the download: 44908
' Downloads complete.