Delen via


Procedure: Het overzicht van Async uitbreiden met behulp van Task.WhenAll (Visual Basic)

U kunt de prestaties van de asynchrone oplossing in Walkthrough verbeteren: Het web openen met behulp van Async en Await (Visual Basic) met behulp van de Task.WhenAll methode. Deze methode wacht asynchroon op meerdere asynchrone bewerkingen, die worden weergegeven als een verzameling taken.

Mogelijk hebt u in het overzicht opgemerkt dat de websites met verschillende tarieven worden gedownload. Soms is een van de websites erg traag, waardoor alle resterende downloads worden vertraagd. Wanneer u de asynchrone oplossingen uitvoert die u in het scenario bouwt, kunt u het programma eenvoudig beëindigen als u niet wilt wachten, maar een betere optie is om alle downloads tegelijkertijd te starten en snellere downloads door te laten gaan zonder te wachten op de vertraagde downloads.

U past de Task.WhenAll methode toe op een verzameling taken. De toepassing van WhenAll retourneert één taak die niet is voltooid totdat elke taak in de verzameling is voltooid. De taken lijken parallel te worden uitgevoerd, maar er worden geen extra threads gemaakt. De taken kunnen in elke volgorde worden voltooid.

Belangrijk

In de volgende procedures worden extensies beschreven voor de asynchrone toepassingen die zijn ontwikkeld in Walkthrough: Accessing the Web by Using Async and Await (Visual Basic). U kunt de toepassingen ontwikkelen door het overzicht te voltooien of het voorbeeld te downloaden vanuit de .NET Sample Browser. De voorbeeldcode bevindt zich in het SerialAsyncExample-project .

Als u het voorbeeld wilt uitvoeren, moet Visual Studio 2012 of hoger op uw computer zijn geïnstalleerd.

Task.WhenAll toevoegen aan uw GetURLContentsAsync-oplossing

  1. Voeg de ProcessURLAsync methode toe aan de eerste toepassing die is ontwikkeld in Walkthrough: Accessing the Web by Using Async and Await (Visual Basic).

    • Als u de code hebt gedownload uit codevoorbeelden voor ontwikkelaars, opent u het AsyncWalkthrough-project en voegt u deze toe ProcessURLAsync aan het MainWindow.xaml.vb-bestand.

    • Als u de code hebt ontwikkeld door de procedure te voltooien, voegt u deze toe ProcessURLAsync aan de toepassing die de GetURLContentsAsync methode bevat. Het MainWindow.xaml.vb-bestand voor deze toepassing is het eerste voorbeeld in de sectie Complete Code Examples from the Walkthrough.

    De ProcessURLAsync methode consolideert de acties in de hoofdtekst van de For Each lus in SumPageSizesAsync de oorspronkelijke procedure. De methode downloadt asynchroon de inhoud van een opgegeven website als een bytematrix, waarna de lengte van de bytematrix wordt weergegeven en geretourneerd.

    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. Markeer of verwijder de For Each lus in SumPageSizesAsync, zoals in de volgende code wordt weergegeven.

    '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. Maak een verzameling taken. De volgende code definieert een query die, wanneer deze wordt uitgevoerd door de ToArray methode, een verzameling taken maakt waarmee de inhoud van elke website wordt gedownload. De taken worden gestart wanneer de query wordt geëvalueerd.

    Voeg de volgende code toe aan de methode SumPageSizesAsync na de declaratie van 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. Toepassen Task.WhenAll op de verzameling taken, downloadTasks. Task.WhenAll retourneert één taak die eindigt wanneer alle taken in de verzameling taken zijn voltooid.

    In het volgende voorbeeld wacht de Await expressie op de voltooiing van de enkele taak die WhenAll wordt geretourneerd. De expressie evalueert naar een matrix met gehele getallen, waarbij elk geheel getal de lengte is van een gedownloade website. Voeg de volgende code toe aan SumPageSizesAsync, net na de code die u in de vorige stap hebt toegevoegd.

    ' 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. Gebruik ten slotte de Sum methode om de som van de lengten van alle websites te berekenen. Voeg de volgende regel toe aan SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Task.WhenAll toevoegen aan de oplossing HttpClient.GetByteArrayAsync

  1. Voeg de volgende versie toe van ProcessURLAsync de tweede toepassing die is ontwikkeld in Walkthrough: Accessing the Web by Using Async and Await (Visual Basic).

    • Als u de code hebt gedownload uit voorbeelden van ontwikkelaarscode, opent u het AsyncWalkthrough_HttpClient project en voegt u deze vervolgens toe ProcessURLAsync aan het MainWindow.xaml.vb-bestand.

    • Als u de code hebt ontwikkeld door de procedure te voltooien, voegt u deze toe ProcessURLAsync aan de toepassing die gebruikmaakt van de HttpClient.GetByteArrayAsync methode. Het MainWindow.xaml.vb-bestand voor deze toepassing is het tweede voorbeeld in de sectie Complete Code Examples from the Walkthrough.

    De ProcessURLAsync methode consolideert de acties in de hoofdtekst van de For Each lus in SumPageSizesAsync de oorspronkelijke procedure. De methode downloadt asynchroon de inhoud van een opgegeven website als een bytematrix, waarna de lengte van de bytematrix wordt weergegeven en geretourneerd.

    Het enige verschil met de ProcessURLAsync methode in de vorige procedure is het gebruik van het HttpClient exemplaar. 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. Markeer of verwijder de For Each lus in SumPageSizesAsync, zoals in de volgende code wordt weergegeven.

    '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. Definieer een query die, wanneer deze wordt uitgevoerd door de ToArray methode, een verzameling taken maakt waarmee de inhoud van elke website wordt gedownload. De taken worden gestart wanneer de query wordt geëvalueerd.

    Voeg de volgende code toe aan de methode SumPageSizesAsync na de declaratie van client en 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. Vervolgens kunt u de Task.WhenAll verzameling taken toepassen. downloadTasks Task.WhenAll retourneert één taak die eindigt wanneer alle taken in de verzameling taken zijn voltooid.

    In het volgende voorbeeld wacht de Await expressie op de voltooiing van de enkele taak die WhenAll wordt geretourneerd. Als u klaar bent, wordt de Await expressie geëvalueerd in een matrix met gehele getallen, waarbij elk geheel getal de lengte is van een gedownloade website. Voeg de volgende code toe aan SumPageSizesAsync, net na de code die u in de vorige stap hebt toegevoegd.

    ' 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. Gebruik ten slotte de Sum methode om de som van de lengten van alle websites op te halen. Voeg de volgende regel toe aan SumPageSizesAsync.

    Dim total = lengths.Sum()
    

De oplossingen Task.WhenAll testen

Kies voor beide oplossingen de F5-toets om het programma uit te voeren en kies vervolgens de knop Start . De uitvoer moet lijken op de uitvoer van de asynchrone oplossingen in Walkthrough: Accessing the Web by Using Async and Await (Visual Basic). U ziet echter dat de websites elke keer in een andere volgorde worden weergegeven.

Voorbeeld 1

De volgende code toont de extensies voor het project die de GetURLContentsAsync methode gebruikt om inhoud van het web te downloaden.

' 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

Voorbeeld 2

In de volgende code ziet u de extensies voor het project waarin gebruik wordt gemaakt van de methode HttpClient.GetByteArrayAsync voor het downloaden van inhoud van het 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

Zie ook