Compartir a través de


Cómo: Extender el tutorial asincrónico mediante Task.WhenAll (Visual Basic)

Puede mejorar el rendimiento de la solución asincrónica en Walkthrough: Accessing the Web by Using Async and Await (Visual Basic) by using the Task.WhenAll method. Este método espera asincrónicamente varias operaciones asincrónicas, que se representan como una colección de tareas.

Es posible que haya observado en el tutorial que los sitios web se descargan a diferentes tarifas. A veces uno de los sitios web es muy lento, lo que retrasa todas las descargas restantes. Al ejecutar las soluciones asincrónicas que compila en el tutorial, puede finalizar el programa fácilmente si no desea esperar, pero una mejor opción sería iniciar todas las descargas al mismo tiempo y dejar que las descargas más rápidas continúen sin esperar a que se retrase.

El método se aplica Task.WhenAll a una colección de tareas. La aplicación de WhenAll devuelve una sola tarea que no se completa hasta que se completan todas las tareas de la colección. Las tareas parecen ejecutarse en paralelo, pero no se crean subprocesos adicionales. Las tareas se pueden completar en cualquier orden.

Importante

En los procedimientos siguientes se describen las extensiones a las aplicaciones asincrónicas desarrolladas en Walkthrough: Accessing the Web by Using Async and Await (Visual Basic). Puede desarrollar las aplicaciones completando el tutorial o descargando el código del explorador de ejemplos de .NET. El código de ejemplo está en el proyecto SerialAsyncExample.

Para ejecutar el ejemplo, debe tener Instalado Visual Studio 2012 o posterior en el equipo.

Para agregar Task.WhenAll a la solución GetURLContentsAsync

  1. Agregue el ProcessURLAsync método a la primera aplicación desarrollada en Walkthrough: Accessing the Web by Using Async and Await (Visual Basic).

    • Si descargó el código de Ejemplos de código para desarrolladores, abra el proyecto AsyncWalkthrough y ProcessURLAsync agregue al archivo MainWindow.xaml.vb.

    • Si desarrolló el código completando el tutorial, agregue ProcessURLAsync a la aplicación que incluye el GetURLContentsAsync método . El archivo MainWindow.xaml.vb para esta aplicación es el primer ejemplo de la sección "Ejemplos de código completos del tutorial".

    El ProcessURLAsync método consolida las acciones en el cuerpo del For Each bucle en SumPageSizesAsync el tutorial original. El método descarga de forma asincrónica el contenido de un sitio web especificado como una matriz de bytes y, a continuación, muestra y devuelve la longitud de la matriz de bytes.

    Private Async Function ProcessURLAsync(url As String) As Task(Of Integer)
    
        Dim byteArray = Await GetURLContentsAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function
    
  2. Convierta en comentario o elimine el For Each bucle en , como se muestra en SumPageSizesAsyncel código siguiente.

    'Dim total = 0
    'For Each url In urlList
    
    '    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    '    ' The previous line abbreviates the following two assignment statements.
    
    '    ' GetURLContentsAsync returns a task. At completion, the task
    '    ' produces a byte array.
    '    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
    '    'Dim urlContents As Byte() = Await getContentsTask
    
    '    DisplayResults(url, urlContents)
    
    '    ' Update the total.
    '    total += urlContents.Length
    'Next
    
  3. Cree una colección de tareas. El código siguiente define una consulta que, cuando se ejecuta mediante el ToArray método , crea una colección de tareas que descargan el contenido de cada sitio web. Las tareas se inician cuando se evalúa la consulta.

    Agregue el código siguiente al método SumPageSizesAsync después de la declaración de urlList.

    ' Create a query.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url)
    
    ' Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. Aplicar Task.WhenAll a la colección de tareas, downloadTasks. Task.WhenAll devuelve una sola tarea que finaliza cuando se han completado todas las tareas de la colección de tareas.

    En el ejemplo siguiente, la Await expresión espera la finalización de la tarea única que WhenAll devuelve. La expresión se evalúa como una matriz de enteros, donde cada entero es la longitud de un sitio web descargado. Agregue el código siguiente a SumPageSizesAsync, justo después del código que agregó en el paso anterior.

    ' Await the completion of all the running tasks.
    Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)
    
    '' The previous line is equivalent to the following two statements.
    'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
    'Dim lengths As Integer() = Await whenAllTask
    
  5. Por último, use el Sum método para calcular la suma de las longitudes de todos los sitios web. Agregue la siguiente línea a SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Para agregar Task.WhenAll a la solución HttpClient.GetByteArrayAsync

  1. Agregue la siguiente versión de ProcessURLAsync a la segunda aplicación desarrollada en Walkthrough: Accessing the Web by Using Async and Await (Visual Basic).

    • Si descargó el código de Ejemplos de código para desarrolladores, abra el proyecto de AsyncWalkthrough_HttpClient y, a continuación, agregue ProcessURLAsync al archivo MainWindow.xaml.vb.

    • Si desarrolló el código completando el tutorial, agregue ProcessURLAsync a la aplicación que usa el HttpClient.GetByteArrayAsync método . El archivo MainWindow.xaml.vb para esta aplicación es el segundo ejemplo de la sección "Ejemplos de código completos del tutorial".

    El ProcessURLAsync método consolida las acciones en el cuerpo del For Each bucle en SumPageSizesAsync el tutorial original. El método descarga de forma asincrónica el contenido de un sitio web especificado como una matriz de bytes y, a continuación, muestra y devuelve la longitud de la matriz de bytes.

    La única diferencia del ProcessURLAsync método en el procedimiento anterior es el uso de la HttpClient instancia, client.

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)
    
        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function
    
  2. Convierta en comentario o elimine el For Each bucle en , como se muestra en SumPageSizesAsyncel código siguiente.

    'Dim total = 0
    'For Each url In urlList
    '    ' GetByteArrayAsync returns a task. At completion, the task
    '    ' produces a byte array.
    '    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
    '    ' The following two lines can replace the previous assignment statement.
    '    'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
    '    'Dim urlContents As Byte() = Await getContentsTask
    
    '    DisplayResults(url, urlContents)
    
    '    ' Update the total.
    '    total += urlContents.Length
    'Next
    
  3. Defina una consulta que, cuando se ejecuta mediante el ToArray método , crea una colección de tareas que descargan el contenido de cada sitio web. Las tareas se inician cuando se evalúa la consulta.

    Agregue el código siguiente al método SumPageSizesAsync después de la declaración de client y urlList.

    ' Create a query.
    Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
        From url In urlList Select ProcessURLAsync(url, client)
    
    ' Use ToArray to execute the query and start the download tasks.
    Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
    
  4. A continuación, aplique Task.WhenAll a la colección de tareas, downloadTasks. Task.WhenAll devuelve una sola tarea que finaliza cuando se han completado todas las tareas de la colección de tareas.

    En el ejemplo siguiente, la Await expresión espera la finalización de la tarea única que WhenAll devuelve. Cuando se completa, la Await expresión se evalúa como una matriz de enteros, donde cada entero es la longitud de un sitio web descargado. Agregue el código siguiente a SumPageSizesAsync, justo después del código que agregó en el paso anterior.

    ' Await the completion of all the running tasks.
    Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)
    
    '' The previous line is equivalent to the following two statements.
    'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
    'Dim lengths As Integer() = Await whenAllTask
    
  5. Por último, use el Sum método para obtener la suma de las longitudes de todos los sitios web. Agregue la siguiente línea a SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Para probar las soluciones Task.WhenAll

Para cualquiera de las soluciones, elija la tecla F5 para ejecutar el programa y, a continuación, elija el botón Iniciar . La salida debe ser similar a la salida de las soluciones asincrónicas de Walkthrough: Accessing the Web by Using Async and Await (Visual Basic). Sin embargo, observe que los sitios web aparecen en un orden diferente cada vez.

Ejemplo 1

En el código siguiente se muestran las extensiones del proyecto que usa el GetURLContentsAsync método para descargar contenido de la web.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    End Sub

    Private Async Function SumPageSizesAsync() As Task

        ' Make a list of web addresses.
        Dim urlList As List(Of String) = SetUpURLList()

        ' Create a query.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url)

        ' Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' You can do other work here before awaiting.

        ' Await the completion of all the running tasks.
        Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)

        '' The previous line is equivalent to the following two statements.
        'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
        'Dim lengths As Integer() = Await whenAllTask

        Dim total = lengths.Sum()

        'Dim total = 0
        'For Each url In urlList

        '    Dim urlContents As Byte() = Await GetURLContentsAsync(url)

        '    ' The previous line abbreviates the following two assignment statements.

        '    ' GetURLContentsAsync returns a task. At completion, the task
        '    ' produces a byte array.
        '    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
        '    'Dim urlContents As Byte() = Await getContentsTask

        '    DisplayResults(url, urlContents)

        '    ' Update the total.
        '    total += urlContents.Length
        'NextNext

        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "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

    ' The actions from the foreach loop are moved to this async method.
    Private Async Function ProcessURLAsync(url As String) As Task(Of Integer)

        Dim byteArray = Await GetURLContentsAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content.
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL.
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for
        ' the response.
        Using response As WebResponse = Await webReq.GetResponseAsync()
            ' Get the data stream that is associated with the specified URL.
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.
                ' CopyToAsync returns a Task, not a Task<T>.
                Await responseStream.CopyToAsync(content)
            End Using
        End Using

        ' Return the result as a byte array.
        Return content.ToArray()
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format
        ' is designed to be used with a monospaced font, such as
        ' Lucida Console or Global Monospace.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

Ejemplo 2

En el código siguiente se muestran las extensiones del proyecto que usa el método HttpClient.GetByteArrayAsync para descargar contenido de la web.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."
    End Sub

    Private Async Function SumPageSizesAsync() As Task

        ' Declare an HttpClient object and increase the buffer size. The
        ' default buffer size is 65,536.
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

        ' Make a list of web addresses.
        Dim urlList As List(Of String) = SetUpURLList()

        ' Create a query.
        Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
            From url In urlList Select ProcessURLAsync(url, client)

        ' Use ToArray to execute the query and start the download tasks.
        Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()

        ' You can do other work here before awaiting.

        ' Await the completion of all the running tasks.
        Dim lengths As Integer() = Await Task.WhenAll(downloadTasks)

        '' The previous line is equivalent to the following two statements.
        'Dim whenAllTask As Task(Of Integer()) = Task.WhenAll(downloadTasks)
        'Dim lengths As Integer() = Await whenAllTask

        Dim total = lengths.Sum()

        ''<snippet7>
        'Dim total = 0
        'For Each url In urlList
        '    ' GetByteArrayAsync returns a task. At completion, the task
        '    ' produces a byte array.
        '    '<snippet31>
        '    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
        '    '</snippet31>

        '    ' The following two lines can replace the previous assignment statement.
        '    'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
        '    'Dim urlContents As Byte() = Await getContentsTask

        '    DisplayResults(url, urlContents)

        '    ' Update the total.
        '    total += urlContents.Length
        'NextNext

        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://www.msdn.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "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

    Private Async Function ProcessURLAsync(url As String, client As HttpClient) As Task(Of Integer)

        Dim byteArray = Await client.GetByteArrayAsync(url)
        DisplayResults(url, byteArray)
        Return byteArray.Length
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format
        ' is designed to be used with a monospaced font, such as
        ' Lucida Console or Global Monospace.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

Consulte también