Start Multiple Async Tasks and Process Them As They Complete (Visual Basic) (Inicio de varias tareas asincrónicas y cómo procesarlas a medida que se completan [Visual Basic])

Si usa Task.WhenAny, puede iniciar varias tareas a la vez y procesarlas una por una a medida que se completen, en lugar de procesarlas en el orden en el que se han iniciado.

En el siguiente ejemplo se usa una consulta para crear una colección de tareas. Cada tarea descarga el contenido de un sitio web especificado. En cada iteración de un bucle while, una llamada awaited a WhenAny devuelve la tarea en la colección de tareas que termine primero su descarga. Esa tarea se quita de la colección y se procesa. El bucle se repite hasta que la colección no contiene más tareas.

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.

  1. Descomprima el archivo descargado y, a continuación, inicie Visual Studio.

  2. En la barra de menús, elija Archivo, Abrir, Proyecto o solución.

  3. 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.

  4. En el Explorador de soluciones, abra el menú contextual del proyecto ProcessTasksAsTheyFinish y, después, pulse Establecer como proyecto de inicio.

  5. Pulse la tecla F5 para ejecutar el proyecto.

    Presione las teclas Ctrl+F5 para ejecutar el proyecto sin depurarlo.

  6. Ejecute el proyecto varias veces para comprobar que las longitudes que se han descargado no aparecen siempre en el mismo orden.

Si no desea descargar el proyecto, puede revisar el archivo MainWindow.xaml.vb al final de este tema.

Compilación del ejemplo

Este ejemplo se agrega al código desarrollado en Cancelar las tareas asincrónicas restantes cuando se completa una (Visual Basic) y usa la misma interfaz de usuario.

Para generar el ejemplo personalmente, paso a paso, siga las instrucciones de la sección "Descargar el ejemplo", pero elija CancelAfterOneTask como el Proyecto de inicio. Agregue los cambios de este tema al método AccessTheWebAsync de ese proyecto. Los cambios se marcan con asteriscos.

El proyecto CancelAfterOneTask ya incluye una consulta que, cuando se ejecuta, crea una colección de tareas. Cada llamada a ProcessURLAsync en el siguiente código devuelve un objeto Task<TResult> donde TResult es un entero.

Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =  
    From url In urlList Select ProcessURLAsync(url, client, ct)  

En el archivo MainWindow.xaml.vb del proyecto, realice los siguientes cambios en el método AccessTheWebAsync.

  • Ejecute la consulta aplicando Enumerable.ToList en lugar de ToArray.

    Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()  
    
  • Agregue un bucle while que realice los pasos siguientes para cada tarea de la colección.

    1. Espera una llamada a WhenAny para identificar la primera tarea en la colección para finalizar su descarga.

      Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)  
      
    2. Quita la tarea de la colección.

      downloadTasks.Remove(finishedTask)  
      
    3. Espera finishedTask, que se devuelve mediante una llamada a ProcessURLAsync. La variable finishedTask es un Task<TResult> donde TReturn es un entero. La tarea ya está completa, pero la espera para recuperar la longitud del sitio web descargado, como se muestra en el ejemplo siguiente.

      Dim length = Await finishedTask  
      resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website:  {0}" & vbCrLf, length)  
      

Debe ejecutar el proyecto varias veces para comprobar que las longitudes que se han descargado no aparecen siempre en el mismo orden.

Precaución

Puede usar WhenAny en un bucle, como se describe en el ejemplo, para solucionar problemas que implican un número reducido de tareas. Sin embargo, otros enfoques son más eficaces si hay que procesar un gran número de tareas. Para obtener más información y ejemplos, vea Processing Tasks as they complete (Procesamiento de tareas a medida que se completan).

Ejemplo completo

El código siguiente es el texto completo del archivo MainWindow.xaml.vb 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 & "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.  

Consulte también