Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Mediante el uso del método Task.WhenAny junto con CancellationToken, puede cancelar todas las tareas restantes cuando se completa una. El WhenAny
método toma un argumento que es una colección de tareas. El método inicia todas las tareas y devuelve una sola tarea. La tarea única se completa cuando se completa cualquier tarea de la colección.
En este ejemplo se muestra cómo usar un token de cancelación junto con WhenAny
para quedarse con la primera tarea que finalice 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 para completar y cancelar las demás descargas.
Nota:
Para ejecutar los ejemplos, debe tener Visual Studio 2012 o versiones posteriores y .NET Framework 4.5 o posterior instalado en el equipo.
Descarga del ejemplo
Puede descargar el proyecto completo de Windows Presentation Foundation (WPF) de Async Sample: Fine Tuning Your Application y luego siga estos pasos.
Descomprima el archivo que descargó e 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, a continuación, abra el archivo de solución (.sln) para AsyncFineTuningVB.
En el Explorador de soluciones, abra el menú contextual del proyecto CancelAfterOneTask y elija Establecer como proyecto de inicio.
Elija 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 las distintas descargas finalizan primero.
Si no desea descargar el proyecto, puede revisar el archivo MainWindow.xaml.vb al final de este tema.
Creación del ejemplo
El ejemplo en este tema se añade al proyecto que se desarrolla en Cancelar una tarea asincrónica o una lista de tareas, 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 compilar el ejemplo usted mismo, 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 de cada sitio web desde el bucle al siguiente método asincrónico AccessTheWebAsync
.
' ***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
, en este ejemplo se usa una consulta, el ToArray método y el WhenAny
método 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 cambios siguientes 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 objeto 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
ToArray
a para ejecutar la consulta e iniciar las tareas. La aplicación delWhenAny
método en el paso siguiente ejecutaría la consulta e iniciaría las tareas sin usarToArray
, pero es posible que otros métodos no. La práctica más segura es forzar la ejecución de la consulta explícitamente.' ***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 unTask(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 porque 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
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 las distintas descargas finalizan primero.
Ejemplo completo
El código siguiente es el archivo completo MainWindow.xaml.vb o MainWindow.xaml.cs 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.