Partager via


Procédure : étendre la procédure pas à pas asynchrone à l’aide de Task.WhenAll (Visual Basic)

Vous pouvez améliorer les performances de la solution asynchrone dans la procédure pas à pas : accès au web à l’aide d’Async et Await (Visual Basic) à l’aide de la Task.WhenAll méthode. Cette méthode attend de façon asynchrone plusieurs opérations asynchrones, qui sont représentées sous la forme d’une collection de tâches.

Vous avez peut-être remarqué dans la procédure pas à pas que les sites web téléchargent à différents taux. Parfois, l’un des sites web est très lent, ce qui retarde tous les téléchargements restants. Lorsque vous exécutez les solutions asynchrones que vous générez dans la procédure pas à pas, vous pouvez mettre fin au programme facilement si vous ne souhaitez pas attendre, mais une meilleure option consiste à démarrer tous les téléchargements en même temps et à laisser des téléchargements plus rapides continuer sans attendre celui qui est retardé.

Vous appliquez la Task.WhenAll méthode à une collection de tâches. L’application de WhenAll retourne une tâche unique qui n’est pas terminée tant que chaque tâche de la collection n’est pas terminée. Les tâches semblent s’exécuter en parallèle, mais aucun thread supplémentaire n’est créé. Les tâches peuvent se terminer dans n’importe quel ordre.

Importante

Les procédures suivantes décrivent les extensions des applications asynchrones développées dans la procédure pas à pas : accès au web à l’aide d’Async et Await (Visual Basic). Vous pouvez développer les applications en effectuant la procédure pas à pas ou en téléchargeant l’exemple à partir du .NET Sample Browser. L’exemple de code se trouve dans le projet SerialAsyncExample .

Pour exécuter l’exemple, visual Studio 2012 ou version ultérieure doit être installé sur votre ordinateur.

Pour ajouter Task.WhenAll à votre solution GetURLContentsAsync

  1. Ajoutez la ProcessURLAsync méthode à la première application développée dans la procédure pas à pas : accès au web à l’aide d’Async et Await (Visual Basic).

    • Si vous avez téléchargé le code à partir d’exemples de code développeur, ouvrez le projet AsyncWalkthrough, puis ajoutez-y ProcessURLAsync le fichier MainWindow.xaml.vb.

    • Si vous avez développé le code en effectuant la procédure pas à pas, ajoutez ProcessURLAsync à l’application qui inclut la GetURLContentsAsync méthode. Le fichier MainWindow.xaml.vb de cette application est le premier exemple de la section « Exemples de code complets à partir de la procédure pas à pas ».

    La méthode ProcessURLAsync consolide les actions dans le corps de la boucle For Each dans SumPageSizesAsync dans la procédure d’origine. La méthode télécharge de façon asynchrone le contenu d’un site web spécifié en tant que tableau d’octets, puis affiche et retourne la longueur du tableau d’octets.

    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. Commentez ou supprimez la For Each boucle dans SumPageSizesAsync, comme le montre le code suivant.

    '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. Créez une collection de tâches. Le code suivant définit une requête qui, lorsqu’elle est exécutée par la ToArray méthode, crée une collection de tâches qui téléchargent le contenu de chaque site web. Les tâches sont démarrées lorsque la requête est évaluée.

    Ajoutez le code suivant à la méthode SumPageSizesAsync après la déclaration 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. Appliquez Task.WhenAll à la collection de tâches. downloadTasks Task.WhenAll retourne une tâche unique qui se termine lorsque toutes les tâches de la collection de tâches sont terminées.

    Dans l’exemple suivant, l’expression Await attend l’achèvement de la tâche unique retournée par WhenAll. L’expression prend la valeur d’un tableau d’entiers, où chaque entier est la longueur d’un site web téléchargé. Ajoutez le code suivant à SumPageSizesAsync, juste après le code que vous avez ajouté à l’étape précédente.

    ' 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. Enfin, utilisez la Sum méthode pour calculer la somme des longueurs de tous les sites web. Ajoutez la ligne suivante à SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Pour ajouter Task.WhenAll à la solution HttpClient.GetByteArrayAsync

  1. Ajoutez la version suivante de ProcessURLAsync à la deuxième application développée dans Procédure pas à pas : accès au web avec Async et Await (Visual Basic).

    • Si vous avez téléchargé le code à partir d’exemples de code développeur, ouvrez le projet AsyncWalkthrough_HttpClient, puis ajoutez-y ProcessURLAsync le fichier MainWindow.xaml.vb.

    • Si vous avez développé le code en effectuant la procédure pas à pas, ajoutez ProcessURLAsync à l’application qui utilise la HttpClient.GetByteArrayAsync méthode. Le fichier MainWindow.xaml.vb de cette application est le deuxième exemple de la section « Exemples de code complets à partir de la procédure pas à pas ».

    La méthode ProcessURLAsync consolide les actions dans le corps de la boucle For Each dans SumPageSizesAsync dans la procédure d’origine. La méthode télécharge de façon asynchrone le contenu d’un site web spécifié en tant que tableau d’octets, puis affiche et retourne la longueur du tableau d’octets.

    La seule différence par rapport à la ProcessURLAsync méthode dans la procédure précédente est l'utilisation de l'instance HttpClient, 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. Commentez ou supprimez la For Each boucle dans SumPageSizesAsync, comme le montre le code suivant.

    '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. Définissez une requête qui, lorsqu’elle est exécutée par la ToArray méthode, crée une collection de tâches qui téléchargent le contenu de chaque site web. Les tâches sont démarrées lorsque la requête est évaluée.

    Ajoutez le code suivant à la méthode SumPageSizesAsync après la déclaration de client et 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. Ensuite, appliquez Task.WhenAll à la collection de tâches. downloadTasks Task.WhenAll retourne une tâche unique qui se termine lorsque toutes les tâches de la collection de tâches sont terminées.

    Dans l’exemple suivant, l’expression Await attend l’achèvement de la tâche unique retournée par WhenAll. Une fois terminée, l’expression Await prend la valeur d’un tableau d’entiers, où chaque entier est la longueur d’un site web téléchargé. Ajoutez le code suivant à SumPageSizesAsync, juste après le code que vous avez ajouté à l’étape précédente.

    ' 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. Enfin, utilisez la Sum méthode pour obtenir la somme des longueurs de tous les sites web. Ajoutez la ligne suivante à SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Pour tester les solutions Task.WhenAll

Pour les deux solutions, choisissez la touche F5 pour exécuter le programme, puis choisissez le bouton Démarrer . La sortie doit ressembler à la sortie des solutions asynchrones dans la procédure pas à pas : accès au web à l’aide d’Async et Await (Visual Basic). Toutefois, notez que les sites web apparaissent dans un ordre différent à chaque fois.

Exemple 1

Le code suivant montre les extensions du projet qui utilisent la GetURLContentsAsync méthode pour télécharger du contenu à partir du 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

Exemple 2

Le code suivant montre les extensions du projet qui utilisent la méthode HttpClient.GetByteArrayAsync pour télécharger du contenu à partir du 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

Voir aussi