How to: Extend the Async Walkthrough by Using Task.WhenAll (Visual Basic) (Практическое руководство. Расширение пошагового руководства по асинхронным процедурам с использованием метода Task.WhenAll (Visual Basic))
Вы можете повысить производительность асинхронного решения в пошаговом руководстве. Доступ к Интернету с помощью Async и Await (Visual Basic) с помощью Task.WhenAll метода. Этот метод асинхронно ожидает несколько асинхронных операций, которые представлены в виде коллекции задач.
Как вы могли заметить в этом пошаговом руководстве, веб-сайты загружаются с разной скоростью. Иногда один из веб-сайтов работает слишком медленно, что задерживает все остальные загрузки. Во время работы асинхронных программ, созданных в этом пошаговом руководстве, программу можно с легкостью завершить, если нет необходимости в ожидании, но было бы лучше начать все загрузки одновременно и дать возможность более быстрым загрузкам продолжаться без ожидания более медленных.
Метод Task.WhenAll
можно применить к коллекции задач. Метод WhenAll
, примененный к коллекции, возвращает одну задачу, которая остается незавершенной до тех пор, пока не будет выполнена каждая задача из коллекции. Как видим, задачи выполняются параллельно, однако дополнительные потоки не создаются. Задачи могут выполняться в любом порядке.
Внимание
В следующих процедурах описываются расширения для асинхронных приложений, разработанных в пошаговом руководстве. Доступ к Интернету с помощью Async и Await (Visual Basic). Вы можете разрабатывать приложения, выполнив пошаговое руководство или скачав пример из браузера .NET Sample Browser. Пример кода находится в проекте SerialAsyncExample .
Для выполнения этого примера на компьютере должна быть установлена среда Visual Studio 2012 или более поздней версии.
Добавление метода Task.WhenAll в решение GetURLContentsAsync
ProcessURLAsync
Добавьте метод в первое приложение, разработанное в пошаговом руководстве. Доступ к Интернету с помощью Async и Await (Visual Basic).Если вы скачали код из примеров кода разработчика, откройте проект AsyncWalkthrough, а затем добавьте
ProcessURLAsync
в файл MainWindow.xaml.vb.Если вы разработали код, выполнив пошаговое руководство, добавьте
ProcessURLAsync
в приложение, которое включает методGetURLContentsAsync
. Файл MainWindow.xaml.vb для этого приложения является первым примером в разделе "Полные примеры кода из пошагового руководства".
Метод
ProcessURLAsync
объединяет действия в теле циклаFor Each
вSumPageSizesAsync
в исходном пошаговом руководстве. Метод асинхронно скачивает содержимое указанного веб-сайта в виде массива байтов и затем отображает и возвращает длину массива байтов.Private Async Function ProcessURLAsync(url As String) As Task(Of Integer) Dim byteArray = Await GetURLContentsAsync(url) DisplayResults(url, byteArray) Return byteArray.Length End Function
Закомментируйте или удалите цикл
For Each
вSumPageSizesAsync
, как показано в следующем коде.'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
Создайте коллекцию задач. Следующий код определяет запрос, который при выполнении метода ToArray создает коллекцию задач, скачивающих содержимое каждого веб-сайта. Задачи запускаются при вычислении запроса.
Добавьте следующий код в метод
SumPageSizesAsync
после объявления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()
Примените
Task.WhenAll
к коллекции задач,downloadTasks
.Task.WhenAll
возвращает одну задачу, которая завершается после завершения всех задач в коллекции задач.В следующем примере выражение
Await
ожидает завершения одной задачи, возвращаемойWhenAll
. Результат этого выражения – массив целых чисел, каждое из которых – размер загруженного веб-сайта. Добавьте следующий код вSumPageSizesAsync
сразу после кода, добавленного на предыдущем шаге.' 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
И, наконец, используйте метод Sum для вычисления суммы длин всех веб-сайтов. Добавьте следующую строку в
SumPageSizesAsync
.Dim total = lengths.Sum()
Добавление метода Task.WhenAll в решение HttpClient.GetByteArrayAsync
Добавьте следующую версию второго приложения, разработанного
ProcessURLAsync
в пошаговом руководстве: доступ к Интернету с помощью Async и Await (Visual Basic).Если вы скачали код из примеров кода разработчика, откройте проект AsyncWalkthrough_HttpClient, а затем добавьте
ProcessURLAsync
в файл MainWindow.xaml.vb.Если вы разработали код, выполнив пошаговое руководство, добавьте
ProcessURLAsync
в приложение, которое использует методHttpClient.GetByteArrayAsync
. Файл MainWindow.xaml.vb для этого приложения является вторым примером в разделе "Полные примеры кода из пошагового руководства".
Метод
ProcessURLAsync
объединяет действия в теле циклаFor Each
вSumPageSizesAsync
в исходном пошаговом руководстве. Метод асинхронно скачивает содержимое указанного веб-сайта в виде массива байтов и затем отображает и возвращает длину массива байтов.Единственное отличие от метода
ProcessURLAsync
из предыдущей процедуры заключается в использовании экземпляра 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
Закомментируйте или удалите цикл
For Each
вSumPageSizesAsync
, как показано в следующем коде.'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
Определите запрос, который при выполнении метода ToArray создает коллекцию задач, скачивающих содержимое каждого веб-сайта. Задачи запускаются при вычислении запроса.
Добавьте следующий код в метод
SumPageSizesAsync
после объявленияclient
и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()
Затем примените
Task.WhenAll
к коллекции задач,downloadTasks
.Task.WhenAll
возвращает одну задачу, которая завершается после завершения всех задач в коллекции задач.В следующем примере выражение
Await
ожидает завершения одной задачи, возвращаемойWhenAll
. Результат выраженияAwait
— массив целых чисел, каждое из которых представляет размер скачанного веб-сайта. Добавьте следующий код вSumPageSizesAsync
сразу после кода, добавленного на предыдущем шаге.' 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
И, наконец, используйте метод Sum для получения суммы длин всех веб-сайтов. Добавьте следующую строку в
SumPageSizesAsync
.Dim total = lengths.Sum()
Тестирование решений Task.WhenAll
Для любого из решений нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Запуск. Выходные данные должны выглядеть как выходные данные из асинхронных решений в пошаговом руководстве. Доступ к Интернету с помощью Async и Await (Visual Basic). Тем не менее обратите внимание, что веб-сайты каждый раз отображаются в другом порядке.
Пример 1
В следующем коде показано расширение для проекта, использующее метод GetURLContentsAsync
для скачивания содержимого из Интернета.
' 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
Пример 2
В следующем коде показано расширение для проекта, использующее метод HttpClient.GetByteArrayAsync
для скачивания содержимого из Интернета.
' 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
См. также
- Task.WhenAll
- Walkthrough: Accessing the Web by Using Async and Await (Visual Basic) (Пошаговое руководство. Доступ к веб-сайтам с помощью модификатора Async и оператора Await (Visual Basic))