次の方法で共有


方法: Task.WhenAll を使用して非同期チュートリアルを拡張する (Visual Basic)

非同期ソリューションのパフォーマンスを向上させるには、「 チュートリアル: Async と Await を使用した Web へのアクセス (Visual Basic)Task.WhenAll メソッドを使用する」を参照してください。 このメソッドは、タスクのコレクションとして表される複数の非同期操作を非同期的に待機します。

このチュートリアルでは、Web サイトが異なる料金でダウンロードされていることに気付いたかもしれません。 場合によっては、Web サイトの 1 つが非常に遅く、残りのすべてのダウンロードが遅くなることがあります。 チュートリアルでビルドした非同期ソリューションを実行すると、待機したくない場合はプログラムを簡単に終了できますが、すべてのダウンロードを同時に開始し、遅延したダウンロードを待たずに高速ダウンロードを続行することをお勧めします。

Task.WhenAll メソッドは、タスクのコレクションに適用します。 WhenAllのアプリケーションは、コレクション内のすべてのタスクが完了するまで完了しない 1 つのタスクを返します。 タスクは並列で実行されているように見えますが、追加のスレッドは作成されません。 タスクは任意の順序で完了できます。

Von Bedeutung

次の手順では、「 チュートリアル: Async と Await を使用した Web へのアクセス (Visual Basic)」で開発された非同期アプリケーションの拡張機能について説明します。 アプリケーションを開発するには、チュートリアルを完了するか、.NET サンプル ブラウザーからサンプルをダウンロードします。 サンプル コードは SerialAsyncExample プロジェクト内にあります。

この例を実行するには、Visual Studio 2012 以降がコンピューターにインストールされている必要があります。

GetURLContentsAsync ソリューションに Task.WhenAll を追加するには

  1. チュートリアル: Async と Await を使用した Web へのアクセス (Visual Basic)」で開発した最初のアプリケーションに、ProcessURLAsync メソッドを追加します。

    • 開発者コード サンプルからコードをダウンロードした場合は、AsyncWalkthrough プロジェクトを開き、MainWindow.xaml.vb ファイルにProcessURLAsyncを追加します。

    • チュートリアルを完了してコードを開発した場合は、GetURLContentsAsync メソッドを含むProcessURLAsyncをアプリケーションに追加します。 このアプリケーションのMainWindow.xaml.vb ファイルは、「チュートリアルの完全なコード例」セクションの最初の例です。

    ProcessURLAsync メソッドは、元のチュートリアルのSumPageSizesAsyncFor Each ループの本文のアクションを統合します。 メソッドは、指定した Web サイトの内容をバイト配列として非同期的にダウンロードし、バイト配列の長さを表示して返します。

    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. 次のコードに示すように、SumPageSizesAsyncFor Each ループをコメント アウトまたは削除します。

    '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. タスクのコレクションを作成します。 次のコードは、ToArray メソッドによって実行されたときに、各 Web サイトの内容をダウンロードするタスクのコレクションを作成するクエリを定義します。 タスクは、クエリの評価時に開始されます。

    urlListの宣言の後に、メソッド SumPageSizesAsyncに次のコードを追加します。

    ' 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. タスクのコレクション (downloadTasks) にTask.WhenAllを適用します。 Task.WhenAll は、タスクのコレクション内のすべてのタスクが完了したときに完了する 1 つのタスクを返します。

    次の例では、 Await 式は、 WhenAll が返す 1 つのタスクの完了を待機しています。 式は整数の配列に評価されます。各整数は、ダウンロードした Web サイトの長さです。 前の手順で追加したコードの直後に、次のコードを 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
    
  5. 最後に、 Sum メソッドを使用して、すべての Web サイトの長さの合計を計算します。 次の行を SumPageSizesAsyncに追加します。

    Dim total = lengths.Sum()
    

Task.WhenAll を HttpClient.GetByteArrayAsync ソリューションに追加するには

  1. 「チュートリアル: Async と Await を使用した Web へのアクセス (Visual Basic)」で開発した 2 番目のアプリケーションに、次のバージョンのProcessURLAsyncを追加します。

    • 開発者コード サンプルからコードをダウンロードした場合は、AsyncWalkthrough_HttpClient プロジェクトを開き、MainWindow.xaml.vb ファイルにProcessURLAsyncを追加します。

    • チュートリアルを完了してコードを開発した場合は、HttpClient.GetByteArrayAsync メソッドを使用するアプリケーションにProcessURLAsyncを追加します。 このアプリケーションのMainWindow.xaml.vb ファイルは、「チュートリアルの完全なコード例」セクションの 2 番目の例です。

    ProcessURLAsync メソッドは、元のチュートリアルのSumPageSizesAsyncFor Each ループの本文のアクションを統合します。 メソッドは、指定した Web サイトの内容をバイト配列として非同期的にダウンロードし、バイト配列の長さを表示して返します。

    前の手順の 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
    
  2. 次のコードに示すように、SumPageSizesAsyncFor Each ループをコメント アウトまたは削除します。

    '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. ToArray メソッドによって実行されたときに、各 Web サイトのコンテンツをダウンロードするタスクのコレクションを作成するクエリを定義します。 タスクは、クエリの評価時に開始されます。

    clienturlListの宣言の後に、メソッド SumPageSizesAsyncに次のコードを追加します。

    ' 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. 次に、タスクのコレクション (downloadTasks) にTask.WhenAllを適用します。 Task.WhenAll は、タスクのコレクション内のすべてのタスクが完了したときに完了する 1 つのタスクを返します。

    次の例では、 Await 式は、 WhenAll が返す 1 つのタスクの完了を待機しています。 完了すると、 Await 式は整数の配列に評価されます。各整数は、ダウンロードした Web サイトの長さです。 前の手順で追加したコードの直後に、次のコードを 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
    
  5. 最後に、 Sum メソッドを使用して、すべての Web サイトの長さの合計を取得します。 次の行を SumPageSizesAsyncに追加します。

    Dim total = lengths.Sum()
    

Task.WhenAll ソリューションをテストするには

どちらのソリューションでも、F5 キーを押してプログラムを実行し、[ スタート ] ボタンを選択します。 出力は、「 チュートリアル: Async と Await を使用した Web へのアクセス (Visual Basic)」の非同期ソリューションからの出力のようになります。 ただし、Web サイトは毎回異なる順序で表示されます。

例 1

次のコードは、 GetURLContentsAsync メソッドを使用して 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

例 2

次のコードは、メソッド HttpClient.GetByteArrayAsync を使用して 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

こちらも参照ください