Cancelar tarefas assíncronas restantes após a conclusão de uma (Visual Basic)
Usando o Task.WhenAny método junto com um CancellationToken, você pode cancelar todas as tarefas restantes quando uma tarefa for concluída. O WhenAny
método usa um argumento que é uma coleção de tarefas. O método inicia todas as tarefas e retorna uma única tarefa. A única tarefa é concluída quando qualquer tarefa na coleção é concluída.
Este exemplo demonstra como usar um token de cancelamento em conjunto com WhenAny
para manter a primeira tarefa a ser concluída a partir da coleção de tarefas e cancelar as tarefas restantes. Cada tarefa descarrega o conteúdo de um website. O exemplo exibe o comprimento do conteúdo do primeiro download a ser concluído e cancela os outros downloads.
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 CancelAfterOneTask 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 programa várias vezes para verificar se diferentes downloads terminam primeiro.
Se não quiser baixar o projeto, você pode revisar o arquivo de MainWindow.xaml.vb no final deste tópico.
Construindo o exemplo
O exemplo neste tópico adiciona ao projeto desenvolvido em Cancelar uma tarefa assíncrona ou uma lista de tarefas para cancelar uma lista de tarefas. O exemplo usa a mesma interface do usuário, embora o botão Cancelar não seja usado explicitamente.
Para construir o exemplo você mesmo, passo a passo, siga as instruções na seção "Baixando o exemplo", mas escolha CancelAListOfTasks como o projeto de inicialização. Adicione as alterações neste tópico a esse projeto.
No arquivo MainWindow.xaml.vb do projeto CancelAListOfTasks , inicie a transição movendo as etapas de processamento para cada site do loop in AccessTheWebAsync
para o seguinte método assíncrono.
' ***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
No AccessTheWebAsync
, este exemplo usa uma consulta, o ToArray método e o WhenAny
método para criar e iniciar uma matriz de tarefas. A aplicação de WhenAny
à matriz retorna uma única tarefa que, quando aguardada, é avaliada até a primeira tarefa a ser concluída na matriz de tarefas.
Faça as seguintes alterações no AccessTheWebAsync
. Asterisks marcar as alterações no arquivo de código.
Comente ou exclua o loop.
Crie uma consulta que, quando executada, produza uma coleção de tarefas genéricas. Cada chamada para
ProcessURLAsync
retorna um Task<TResult> ondeTResult
é um inteiro.' ***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)
Ligue
ToArray
para executar a consulta e iniciar as tarefas. A aplicação doWhenAny
método na próxima etapa executaria a consulta e iniciaria as tarefas sem usarToArray
, mas outros métodos podem não. A prática mais segura é forçar a execução da consulta explicitamente.' ***Use ToArray to execute the query and start the download tasks. Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
Chame
WhenAny
a coleção de tarefas.WhenAny
retorna aTask(Of Task(Of Integer))
ouTask<Task<int>>
. Ou seja,WhenAny
retorna uma tarefa que é avaliada para uma únicaTask(Of Integer)
ouTask<int>
quando é aguardada. Essa única tarefa é a primeira tarefa da coleção a ser concluída. A tarefa concluída primeiro é atribuída afinishedTask
. O tipo de é Task<TResult> ondeTResult
é um inteiro porque esse é o tipo definishedTask
retorno deProcessURLAsync
.' ***Call WhenAny and then await the result. The task that finishes ' first is assigned to finishedTask. Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
Neste exemplo, você está interessado apenas na tarefa que termina primeiro. Portanto, use CancellationTokenSource.Cancel para cancelar as tarefas restantes.
' ***Cancel the rest of the downloads. You just want the first one. cts.Cancel()
Finalmente, aguarde
finishedTask
para recuperar a extensão do conteúdo baixado.Dim length = Await finishedTask resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
Execute o programa várias vezes para verificar se diferentes downloads terminam primeiro.
Exemplo completo
O código a seguir é o arquivo completo MainWindow.xaml.vb ou MainWindow.xaml.cs 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 & "Download complete."
Catch ex As OperationCanceledException
resultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf
Catch ex As Exception
resultsTextBox.Text &= vbCrLf & "Download 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()
'' Comment out or delete the loop.
''For Each url In urlList
'' ' GetAsync returns a Task(Of HttpResponseMessage).
'' ' Argument ct carries the message if the Cancel button is chosen.
'' ' Note that the Cancel button can cancel all remaining downloads.
'' Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)
'' ' Retrieve the website contents from the HttpResponseMessage.
'' Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()
'' resultsTextBox.Text &=
'' vbCrLf & $"Length of the downloaded string: {urlContents.Length}." & vbCrLf
''Next
' ***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 ToArray to execute the query and start the download tasks.
Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
' ***Call WhenAny and then await the result. The task that finishes
' first is assigned to finishedTask.
Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
' ***Cancel the rest of the downloads. You just want the first one.
cts.Cancel()
' ***Await the first completed task and display the results
' Run the program several times to demonstrate that different
' websites can finish first.
Dim length = Await finishedTask
resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
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 downloaded website: 158856
' Download complete.