Cancel Remaining Async Tasks after One Is Complete (Visual Basic) (Cancelación de tareas asincrónicas restantes [Visual Basic])
Mediante el método Task.WhenAny junto con CancellationToken, puede cancelar todas las tareas restantes cuando se completa una tarea. El método WhenAny
toma un argumento que es una colección de tareas. El método inicia todas las tareas y devuelve una sola tarea. La tarea se completa cuando se complete cualquier tarea de la colección.
En este ejemplo se muestra cómo usar un token de cancelación junto con WhenAny
para retener la primera tarea para finalizar de la colección de tareas y cancelar las tareas restantes. Cada tarea descarga el contenido de un sitio web. En el ejemplo se muestra la longitud del contenido de la primera descarga completa y se cancelan las otras descargas.
Nota
Para ejecutar los ejemplos, debe tener Visual Studio 2012 o posterior, y .NET Framework 4.5 o posterior, instalado en el equipo.
Descargar el ejemplo
Puede descargar el proyecto completo de Windows Presentation Foundation (WPF) desde Async Sample: Fine Tuning Your Application (Ejemplo de Async: Ajuste de la aplicación) y después seguir estos pasos.
Descomprima el archivo descargado y, a continuación, inicie Visual Studio.
En la barra de menús, elija Archivo, Abrir, Proyecto o solución.
En el cuadro de diálogo Abrir proyecto, abra la carpeta que contiene el código de ejemplo que descomprimió y después abra el archivo de la solución (.sln) para AsyncFineTuningVB.
En el Explorador de soluciones, abra el menú contextual del proyecto CancelAfterOneTask y, después, elija Establecer como proyecto de inicio.
Pulse la tecla F5 para ejecutar el proyecto.
Presione las teclas Ctrl+F5 para ejecutar el proyecto sin depurarlo.
Ejecute el programa varias veces para comprobar que finalizan primero descargas diferentes.
Si no desea descargar el proyecto, puede revisar el archivo MainWindow.xaml.vb al final de este tema.
Compilación del ejemplo
El ejemplo de este tema se incorpora al proyecto desarrollado en Cancelar una tarea asincrónica o una lista de tareas (C#) para cancelar una lista de tareas. En el ejemplo se usa la misma interfaz de usuario, aunque el botón Cancelar no se usa explícitamente.
Para generar su propio ejemplo, paso a paso, siga las instrucciones de la sección "Descargar el ejemplo", pero elija CancelAListOfTasks como Proyecto de inicio. Agregue los cambios de este tema a ese proyecto.
En el archivo MainWindow.xaml.vb del proyecto CancelAListOfTasks, inicie la transición moviendo los pasos de procesamiento para cada sitio web desde el bucle en AccessTheWebAsync
al siguiente método asincrónico.
' ***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
En AccessTheWebAsync
, este ejemplo usa una consulta, el método ToArray y el método WhenAny
para crear e iniciar una matriz de tareas. La aplicación de WhenAny
a la matriz devuelve una única tarea que, cuando se espera, se evalúa como la primera tarea que llega a la finalización de la matriz de tareas.
Realice los siguientes cambios en AccessTheWebAsync
. Los asteriscos marcan los cambios en el archivo de código.
Convierta en comentario o elimine el bucle.
Cree una consulta que, cuando se ejecute, genere una colección de tareas genéricas. Cada llamada a
ProcessURLAsync
devuelve un Task<TResult> dondeTResult
es un entero.' ***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)
Llame a
ToArray
para ejecutar la consulta e iniciar las tareas. La aplicación del métodoWhenAny
en el paso siguiente ejecutaría la consulta e iniciaría las tareas sin usarToArray
, pero es posible que otros métodos no lo hagan. La práctica más segura es forzar explícitamente la ejecución de la consulta.' ***Use ToArray to execute the query and start the download tasks. Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
Llame a
WhenAny
en la colección de tareas.WhenAny
devuelve unaTask(Of Task(Of Integer))
oTask<Task<int>>
. Es decir,WhenAny
devuelve una tarea que se evalúa como una únicaTask(Of Integer)
oTask<int>
cuando se espera. Esa única tarea es la primera tarea de la colección en finalizar. La tarea que finalizó primero se asigna afinishedTask
. El tipo definishedTask
es Task<TResult>, dondeTResult
es un entero, ya que es el tipo de valor devuelto 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)
En este ejemplo, solo le interesa la tarea que finaliza primero. Por lo tanto, use CancellationTokenSource.Cancel para cancelar las tareas restantes.
' ***Cancel the rest of the downloads. You just want the first one. cts.Cancel()
Por último, espere a
finishedTask
para recuperar la longitud del contenido descargado.Dim length = Await finishedTask resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
Ejecute el programa varias veces para comprobar que finalizan primero descargas diferentes.
Ejemplo completo
El código siguiente es el archivo MainWindow.xaml.vb o MainWindow.xaml.cs completo para el ejemplo. Los asteriscos marcan los elementos que se agregaron para este ejemplo.
Observe que debe agregar una referencia para System.Net.Http.
Puede descargar el proyecto desde Async Sample: Ajuste de la aplicación.
' 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.
Consulte también
- WhenAny
- Fine-Tuning Your Async Application (Visual Basic) (Ajuste de una aplicación asincrónica [Visual Basic])
- Programación asincrónica con Async y Await (Visual Basic)
- Ejemplo de async: Ajuste de la aplicación