Postupy: Rozšíření návodu Async pomocí task.WhenAll (Visual Basic)
Výkon asynchronního řešení můžete zlepšit v návodu: Přístup k webu pomocí Async a Await (Visual Basic) pomocí Task.WhenAll metody. Tato metoda asynchronně čeká na několik asynchronních operací, které jsou reprezentovány jako kolekce úloh.
Možná jste si v návodu všimli, že se weby stahovaly podle různých sazeb. Někdy je jeden z webů velmi pomalý, což zpožďuje všechny zbývající stahování. Když spustíte asynchronní řešení, která sestavíte v návodu, můžete program snadno ukončit, pokud nechcete čekat, ale lepší možností je spustit všechna stahování najednou a nechat rychlejší stahování pokračovat bez čekání na ten, který je zpožděný.
Tuto metodu Task.WhenAll
použijete na kolekci úkolů. Aplikace WhenAll
vrátí jeden úkol, který není dokončen, dokud se nedokončí každý úkol v kolekci. Úlohy se zobrazují paralelně, ale nevytvořila se žádná další vlákna. Úkoly se můžou dokončit v libovolném pořadí.
Důležité
Následující postupy popisují rozšíření asynchronních aplikací vyvinutých v návodu: Přístup k webu pomocí Async a Await (Visual Basic). Aplikace můžete vyvíjet buď dokončením návodu, nebo stažením ukázky z ukázkového prohlížeče .NET. Ukázkový kód je v projektu SerialAsyncExample .
Abyste mohli tento příklad spustit, musíte mít na počítači nainstalovanou sadu Visual Studio 2012 nebo novější.
Přidání metody Task.WhenAll do řešení GetURLContentsAsync
Přidejte metodu
ProcessURLAsync
do první aplikace vyvinuté v návodu: Přístup k webu pomocí Async a Await (Visual Basic).Pokud jste si stáhli kód z ukázek vývojářského kódu, otevřete projekt AsyncWalkthrough a pak ho přidejte
ProcessURLAsync
do souboru MainWindow.xaml.vb.Pokud jste kód vyvinuli dokončením návodu, přidejte
ProcessURLAsync
ho do aplikace, která tuto metoduGetURLContentsAsync
obsahuje. Soubor MainWindow.xaml.vb pro tuto aplikaci je prvním příkladem v části Kompletní příklady kódu z návodu.
Metoda
ProcessURLAsync
konsoliduje akce v těle smyčkyFor Each
vSumPageSizesAsync
původním návodu. Metoda asynchronně stáhne obsah zadaného webu jako bajtové pole a pak zobrazí a vrátí délku pole bajtů.Private Async Function ProcessURLAsync(url As String) As Task(Of Integer) Dim byteArray = Await GetURLContentsAsync(url) DisplayResults(url, byteArray) Return byteArray.Length End Function
Zakomentujte nebo odstraňte smyčku
For Each
,SumPageSizesAsync
jak ukazuje následující kód.'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
Vytvořte kolekci úkolů. Následující kód definuje dotaz , který při spuštění metodou ToArray vytvoří kolekci úloh, které stahují obsah jednotlivých webů. Úkoly se spustí při vyhodnocení dotazu.
Přidejte následující kód do metody
SumPageSizesAsync
za deklaraci .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()
Platí
Task.WhenAll
pro kolekci úkolů .downloadTasks
Task.WhenAll
vrátí jeden úkol, který se dokončí, když jsou dokončeny všechny úkoly v kolekci úkolů.V následujícím příkladu
Await
výraz očekává dokončení jednoho úkolu, kterýWhenAll
vrátí. Výraz se vyhodnotí jako pole celých čísel, kde každé celé číslo je délka staženého webu. Do kódu, který jste přidali v předchozím kroku, přidejte následující kódSumPageSizesAsync
.' 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
Nakonec použijte Sum metodu k výpočtu součtu délek všech webů. Přidejte následující řádek do
SumPageSizesAsync
souboru .Dim total = lengths.Sum()
Přidání metody Task.WhenAll do řešení HttpClient.GetByteArrayAsync
Do druhé aplikace vyvinuté v návodu
ProcessURLAsync
přidejte následující verzi: Přístup k webu pomocí Async a Await (Visual Basic).Pokud jste kód stáhli z ukázek kódu pro vývojáře, otevřete projekt AsyncWalkthrough_HttpClient a pak ho přidejte
ProcessURLAsync
do MainWindow.xaml.vb souboru.Pokud jste kód vyvinuli dokončením návodu, přidejte
ProcessURLAsync
ho do aplikace, která tuto metoduHttpClient.GetByteArrayAsync
používá. Soubor MainWindow.xaml.vb pro tuto aplikaci je druhým příkladem v části Kompletní příklady kódu z návodu.
Metoda
ProcessURLAsync
konsoliduje akce v těle smyčkyFor Each
vSumPageSizesAsync
původním návodu. Metoda asynchronně stáhne obsah zadaného webu jako bajtové pole a pak zobrazí a vrátí délku pole bajtů.Jediným rozdílem
ProcessURLAsync
od metody v předchozím postupu je použití HttpClient instance,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
Zakomentujte nebo odstraňte smyčku
For Each
,SumPageSizesAsync
jak ukazuje následující kód.'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
Definujte dotaz, který při spuštění metodou ToArray vytvoří kolekci úloh, které stahují obsah jednotlivých webů. Úkoly se spustí při vyhodnocení dotazu.
Přidejte následující kód do metody
SumPageSizesAsync
za deklaraciclient
aurlList
.' 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()
Dále platí
Task.WhenAll
pro kolekci úkolůdownloadTasks
.Task.WhenAll
vrátí jeden úkol, který se dokončí, když jsou dokončeny všechny úkoly v kolekci úkolů.V následujícím příkladu
Await
výraz očekává dokončení jednoho úkolu, kterýWhenAll
vrátí. Po dokončeníAwait
se výraz vyhodnotí jako pole celých čísel, kde každé celé číslo je délka staženého webu. Do kódu, který jste přidali v předchozím kroku, přidejte následující kódSumPageSizesAsync
.' 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
Nakonec použijte Sum metodu k získání součtu délky všech webů. Přidejte následující řádek do
SumPageSizesAsync
souboru .Dim total = lengths.Sum()
Otestování řešení Task.WhenAll
V obou řešeních zvolte klávesu F5, aby se program spustil, a pak zvolte tlačítko Start . Výstup by měl vypadat podobně jako výstup z asynchronních řešení v Návodu: Přístup k webu pomocí Async a Await (Visual Basic). Všimněte si ale, že se weby pokaždé zobrazují v jiném pořadí.
Příklad 1
Následující kód ukazuje rozšíření projektu, který používá metodu GetURLContentsAsync
ke stažení obsahu z webu.
' 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
Příklad 2
Následující kód ukazuje rozšíření projektu, který používá metodu HttpClient.GetByteArrayAsync
pro stahování obsahu z webu.
' 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