Cancelar as demais tarefas depois que uma delas estiver concluída (C# e Visual Basic)
Usando o método de Task.WhenAny juntamente com CancellationToken, você pode cancelar todas as outras tarefas quando a tarefa é concluída.O método de WhenAny usa um argumento que é uma coleção de tarefas.O método começa todas as tarefas e retorna uma única tarefa.A única tarefa está completo quando qualquer tarefa na coleção concluída.
Este exemplo demonstra como usar um token cancelar em conjunto com WhenAny para manter a primeira tarefa termina da coleção de tarefas e cancele as tarefas restantes.Cada tarefa baixa o conteúdo de um site.O exemplo exibe o tamanho do conteúdo do primeiro download para concluir e cancelar os outros downloads.
Observação |
---|
Para rodar os exemplos, você deve ter o Visual Studio 2012 O Visual Studio 2012 express para a área de trabalho do Windows, ou o .NET Framework 4.5 instalado no seu computador. |
Fazendo o exemplo
Você pode baixar o projeto completo do Windows Presentation Foundation (WPF) de Exemplo de Async: Ajustando seu aplicativo e então seguir essas etapas.
Descompactar o arquivo que você baixou, e comece em Visual Studio 2012.
Na barra de menu, 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 abra o arquivo de solução (.sln) para AsyncFineTuningCS ou AsyncFineTuningVB.
Em Gerenciador de Soluções, abra o menu de atalho para o projeto de CancelAfterOneTask , e então escolha Definir como projeto de inicialização.
Escolha a tecla F5 para executar o projeto.
Escolha as chaves Ctrl+F5 para executar o projeto sem depurada.
Executar o programa várias vezes verificar que downloads diferentes terminem primeiro.
Se você não deseja baixar o projeto, você pode examinar os arquivos de MainWindow.xaml.vb e de MainWindow.xaml.cs no final deste tópico.
Compilando o exemplo
O exemplo neste tópico adiciona ao projeto que é desenvolvido em Cancelar uma tarefa ou uma lista de tarefas (C# e Visual Basic) para cancelar uma lista de tarefas.O exemplo usa mesmo interface do usuário, embora o botão de Cancelar não é usado explicitamente.
Para criar o exemplo você mesmo, passo a passo, siga as instruções transferência na seção “exemplo”, mas escolher CancelAListOfTasks como Projeto de inicialização.Adicione as alterações neste tópico para o projeto.
No arquivo de MainWindow.xaml.vb ou de MainWindow.xaml.cs de projeto de CancelAListOfTasks , inicie a transição movendo as etapas de processamento para cada site do loop em AccessTheWebAsync ao seguinte método de async.
' ***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
// ***Bundle the processing steps for a website into one async method.
async Task<int> ProcessURLAsync(string url, HttpClient client, CancellationToken ct)
{
// GetAsync returns a Task<HttpResponseMessage>.
HttpResponseMessage response = await client.GetAsync(url, ct);
// Retrieve the website contents from the HttpResponseMessage.
byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
return urlContents.Length;
}
Em AccessTheWebAsync, este exemplo usa uma consulta, o método de ToArray<TSource> , e o método de WhenAny para criar e iniciar uma matriz de tarefas.O aplicativo de WhenAny para o array retorna uma única tarefa que, quando esperada, classifique a primeira tarefa atingir o final na matriz de tarefas.
Faça as seguintes alterações em AccessTheWebAsync.Os asteriscos marcam as alterações no arquivo de código.
Comente out excluir o loop.
Criar uma consulta que, quando executada, gera uma coleção de tarefas genéricos.Cada chamada a ProcessURLAsync retorna Task<TResult>TResult onde é um número 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)
// ***Create a query that, when executed, returns a collection of tasks. IEnumerable<Task<int>> downloadTasksQuery = from url in urlList select ProcessURLAsync(url, client, ct);
Chame ToArray para executar a consulta e para iniciar as tarefas.O aplicativo do método de WhenAny na próxima etapa deseja executar a consulta e era as tarefas sem usar ToArray, mas outros métodos não podem.A prática mais segura é explicitamente forçar a execução da consulta.
' ***Use ToArray to execute the query and start the download tasks. Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
// ***Use ToArray to execute the query and start the download tasks. Task<int>[] downloadTasks = downloadTasksQuery.ToArray();
Chamada WhenAny na coleção de tarefas.WhenAny retorna Task(Of Task(Of Integer)) ou Task<Task<int>>.Isto é, WhenAny retorna uma tarefa que retorna um único Task(Of Integer) ou a Task<int> quando esperado.A única tarefa é a primeira tarefa na coleção concluída.A tarefa que tiver concluído primeiro é atribuída a firstFinishedTask.O tipo de firstFinishedTask é Task<TResult> onde TResult é um inteiro porque é o tipo de retorno de ProcessURLAsync.
' ***Call WhenAny and then await the result. The task that finishes ' first is assigned to firstFinishedTask. Dim firstFinishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
// ***Call WhenAny and then await the result. The task that finishes // first is assigned to firstFinishedTask. Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);
Nesse exemplo, você está apenas interessado na tarefa que conclui primeiro.Como consequência, use CancellationTokenSource.Cancel para cancelar as tarefas restantes.
' ***Cancel the rest of the downloads. You just want the first one. cts.Cancel()
// ***Cancel the rest of the downloads. You just want the first one. cts.Cancel();
Finalmente, espere firstFinishedTask para recuperar o comprimento do conteúdo baixado.
Dim length = Await firstFinishedTask resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
var length = await firstFinishedTask; resultsTextBox.Text += String.Format("\r\nLength of the downloaded website: {0}\r\n", length);
Executar o programa várias vezes verificar que downloads diferentes terminem primeiro.
Exemplo Completo
O código a seguir é o arquivo completo de MainWindow.xaml.vb ou de MainWindow.xaml.cs para o exemplo.Os asteriscos marcam os elementos que foram adicionados para esse exemplo.
Observe que você deve adicionar uma referência para System.Net.Http.
Você pode baixar do Exemplo de Async: Ajustando seu aplicativoprojeto.
' 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 &=
'' String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, urlContents.Length)
''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 firstFinishedTask.
Dim firstFinishedTask 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 firstFinishedTask
resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
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/en-us/library/hh290138.aspx",
"https://msdn.microsoft.com/en-us/library/hh290140.aspx",
"https://msdn.microsoft.com/en-us/library/dd470362.aspx",
"https://msdn.microsoft.com/en-us/library/aa578028.aspx",
"https://msdn.microsoft.com/en-us/library/ms404677.aspx",
"https://msdn.microsoft.com/en-us/library/ff730837.aspx"
}
Return urls
End Function
End Class
' Sample output:
' Length of the downloaded website: 158856
' Download complete.
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;
// Add the following using directive.
using System.Threading;
namespace CancelAfterOneTask
{
public partial class MainWindow : Window
{
// Declare a System.Threading.CancellationTokenSource.
CancellationTokenSource cts;
public MainWindow()
{
InitializeComponent();
}
private async void startButton_Click(object sender, RoutedEventArgs e)
{
// Instantiate the CancellationTokenSource.
cts = new CancellationTokenSource();
resultsTextBox.Clear();
try
{
await AccessTheWebAsync(cts.Token);
resultsTextBox.Text += "\r\nDownload complete.";
}
catch (OperationCanceledException)
{
resultsTextBox.Text += "\r\nDownload canceled.";
}
catch (Exception)
{
resultsTextBox.Text += "\r\nDownload failed.";
}
// Set the CancellationTokenSource to null when the download is complete.
cts = null;
}
// You can still include a Cancel button if you want to.
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}
// Provide a parameter for the CancellationToken.
async Task AccessTheWebAsync(CancellationToken ct)
{
HttpClient client = new HttpClient();
// Call SetUpURLList to make a list of web addresses.
List<string> urlList = SetUpURLList();
// ***Comment out or delete the loop.
//foreach (var url in urlList)
//{
// // GetAsync returns a Task<HttpResponseMessage>.
// // Argument ct carries the message if the Cancel button is chosen.
// // ***Note that the Cancel button can cancel all remaining downloads.
// HttpResponseMessage response = await client.GetAsync(url, ct);
// // Retrieve the website contents from the HttpResponseMessage.
// byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
// resultsTextBox.Text +=
// String.Format("\r\nLength of the downloaded string: {0}.\r\n", urlContents.Length);
//}
// ***Create a query that, when executed, returns a collection of tasks.
IEnumerable<Task<int>> downloadTasksQuery =
from url in urlList select ProcessURLAsync(url, client, ct);
// ***Use ToArray to execute the query and start the download tasks.
Task<int>[] downloadTasks = downloadTasksQuery.ToArray();
// ***Call WhenAny and then await the result. The task that finishes
// first is assigned to firstFinishedTask.
Task<int> firstFinishedTask = 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.
var length = await firstFinishedTask;
resultsTextBox.Text += String.Format("\r\nLength of the downloaded website: {0}\r\n", length);
}
// ***Bundle the processing steps for a website into one async method.
async Task<int> ProcessURLAsync(string url, HttpClient client, CancellationToken ct)
{
// GetAsync returns a Task<HttpResponseMessage>.
HttpResponseMessage response = await client.GetAsync(url, ct);
// Retrieve the website contents from the HttpResponseMessage.
byte[] urlContents = await response.Content.ReadAsByteArrayAsync();
return urlContents.Length;
}
// Add a method that creates a list of web addresses.
private List<string> SetUpURLList()
{
List<string> urls = new List<string>
{
"https://msdn.microsoft.com",
"https://msdn.microsoft.com/en-us/library/hh290138.aspx",
"https://msdn.microsoft.com/en-us/library/hh290140.aspx",
"https://msdn.microsoft.com/en-us/library/dd470362.aspx",
"https://msdn.microsoft.com/en-us/library/aa578028.aspx",
"https://msdn.microsoft.com/en-us/library/ms404677.aspx",
"https://msdn.microsoft.com/en-us/library/ff730837.aspx"
};
return urls;
}
}
// Sample output:
// Length of the downloaded website: 158856
// Download complete.
}
Consulte também
Referência
Conceitos
Ajustando seu aplicativo Async (C# e Visual Basic)
Programação com Async assíncrona e esperar (C# e Visual Basic)