Dela via


Gör så här: Utöka Async-genomgången med hjälp av aktivitet.WhenAll (Visual Basic)

Du kan förbättra prestanda för asynkron lösning i Genomgång: Åtkomst till webben med hjälp av Async och Await (Visual Basic) med hjälp Task.WhenAll av metoden. Den här metoden väntar asynkront på flera asynkrona åtgärder, som representeras som en samling uppgifter.

Du kanske har märkt i genomgången att webbplatserna laddas ned till olika priser. Ibland är en av webbplatserna mycket långsam, vilket fördröjer alla återstående nedladdningar. När du kör de asynkrona lösningar som du skapar i genomgången kan du enkelt avsluta programmet om du inte vill vänta, men ett bättre alternativ är att starta alla nedladdningar samtidigt och låta snabbare nedladdningar fortsätta utan att vänta på den som är försenad.

Du använder metoden för Task.WhenAll en samling uppgifter. Programmet WhenAll returnerar en enskild uppgift som inte har slutförts förrän varje uppgift i samlingen har slutförts. Uppgifterna verkar köras parallellt, men inga ytterligare trådar skapas. Uppgifterna kan slutföras i valfri ordning.

Viktigt!

Följande procedurer beskriver tillägg till de asynkrona program som har utvecklats i Genomgång: Åtkomst till webben med hjälp av Async och Await (Visual Basic). Du kan utveckla programmen genom att antingen slutföra genomgången eller ladda ned exemplet från .NET Sample Browser. Exempelkoden finns i SerialAsyncExample-projektet .

Om du vill köra exemplet måste du ha Visual Studio 2012 eller senare installerat på datorn.

Så här lägger du till Task.WhenAll i din GetURLContentsAsync-lösning

  1. ProcessURLAsync Lägg till metoden i det första programmet som har utvecklats i Genomgång: Åtkomst till webben med hjälp av Async och Await (Visual Basic).

    • Om du har laddat ned koden från Developer Code Samples öppnar du projektet AsyncWalkthrough och lägger sedan till ProcessURLAsync den MainWindow.xaml.vb filen.

    • Om du har utvecklat koden genom att slutföra genomgången lägger du till ProcessURLAsync i programmet som innehåller GetURLContentsAsync -metoden. Den MainWindow.xaml.vb filen för det här programmet är det första exemplet i avsnittet "Slutför kodexempel från genomgången".

    Metoden ProcessURLAsync konsoliderar åtgärderna i loopens For Each brödtext i SumPageSizesAsync den ursprungliga genomgången. Metoden laddar asynkront ned innehållet på en angiven webbplats som en bytematris och visar och returnerar sedan längden på bytematrisen.

    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. Kommentera ut eller ta bort loopen For Each i SumPageSizesAsync, som följande kod visar.

    '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. Skapa en samling uppgifter. Följande kod definierar en fråga som när den ToArray körs med metoden skapar en samling uppgifter som laddar ned innehållet på varje webbplats. Uppgifterna startas när frågan utvärderas.

    Lägg till följande kod i metoden SumPageSizesAsync efter deklarationen av 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. Använd Task.WhenAll för samlingen med uppgifter, downloadTasks. Task.WhenAll returnerar en enskild uppgift som avslutas när alla aktiviteter i samlingen med uppgifter har slutförts.

    I följande exempel Await väntar uttrycket på slutförandet av den enda uppgift som WhenAll returneras. Uttrycket utvärderas till en matris med heltal, där varje heltal är längden på en nedladdad webbplats. Lägg till följande kod i SumPageSizesAsync, strax efter den kod som du lade till i föregående steg.

    ' 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. Slutligen använder du Sum metoden för att beräkna summan av längden på alla webbplatser. Lägg till följande rad i SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Så här lägger du till Task.WhenAll i lösningen HttpClient.GetByteArrayAsync

  1. Lägg till följande version av ProcessURLAsync i det andra programmet som har utvecklats i Genomgång: Åtkomst till webben med hjälp av Async och Await (Visual Basic).

    • Om du har hämtat koden från Developer Code Samples öppnar du projektet AsyncWalkthrough_HttpClient och lägger sedan till ProcessURLAsync den MainWindow.xaml.vb filen.

    • Om du har utvecklat koden genom att slutföra genomgången lägger du till ProcessURLAsync i programmet som använder HttpClient.GetByteArrayAsync -metoden. Den MainWindow.xaml.vb filen för det här programmet är det andra exemplet i avsnittet "Slutför kodexempel från genomgången".

    Metoden ProcessURLAsync konsoliderar åtgärderna i loopens For Each brödtext i SumPageSizesAsync den ursprungliga genomgången. Metoden laddar asynkront ned innehållet på en angiven webbplats som en bytematris och visar och returnerar sedan längden på bytematrisen.

    Den enda skillnaden från ProcessURLAsync metoden i föregående procedur är användningen av instansen 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. Kommentera ut eller ta bort loopen For Each i SumPageSizesAsync, som följande kod visar.

    '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. Definiera en fråga som när den ToArray körs med metoden skapar en samling uppgifter som laddar ned innehållet på varje webbplats. Uppgifterna startas när frågan utvärderas.

    Lägg till följande kod i metoden SumPageSizesAsync efter deklarationen av client och 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. Tillämpa Task.WhenAll sedan på samlingen med uppgifter, downloadTasks. Task.WhenAll returnerar en enskild uppgift som avslutas när alla aktiviteter i samlingen med uppgifter har slutförts.

    I följande exempel Await väntar uttrycket på slutförandet av den enda uppgift som WhenAll returneras. När det Await är klart utvärderas uttrycket till en matris med heltal, där varje heltal är längden på en nedladdad webbplats. Lägg till följande kod i SumPageSizesAsync, strax efter den kod som du lade till i föregående steg.

    ' 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. Slutligen använder du Sum metoden för att få summan av längden på alla webbplatser. Lägg till följande rad i SumPageSizesAsync.

    Dim total = lengths.Sum()
    

Testa Task.WhenAll-lösningar

För någon av lösningarna väljer du F5-nyckeln för att köra programmet och väljer sedan knappen Start . Utdata bör likna utdata från asynkrona lösningar i Genomgång: Åtkomst till webben med hjälp av Async och Await (Visual Basic). Observera dock att webbplatserna visas i en annan ordning varje gång.

Exempel 1

Följande kod visar tilläggen till projektet som använder GetURLContentsAsync metoden för att ladda ned innehåll från webben.

' 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

Exempel 2

Följande kod visar tilläggen till projektet som använder metoden HttpClient.GetByteArrayAsync för att ladda ned innehåll från webben.

' 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

Se även