Task.WhenAnyを使用すると、複数のタスクを同時に開始し、開始された順序で処理するのではなく、完了時に 1 つずつ処理できます。
次の例では、クエリを使用してタスクのコレクションを作成します。 各タスクは、指定された Web サイトの内容をダウンロードします。 while ループの各イテレーションで、 WhenAny
の待機中の呼び出しは、最初にダウンロードを完了するタスクのコレクション内のタスクを返します。 そのタスクはコレクションから削除され、処理されます。 ループは、コレクションにこれ以上タスクが含まれるまで繰り返されます。
注
この例を実行するには、Visual Studio 2012 以降と .NET Framework 4.5 以降がコンピューターにインストールされている必要があります。
サンプルをダウンロード
完全な Windows Presentation Foundation (WPF) プロジェクトは 、非同期サンプル: アプリケーションの微調整 からダウンロードし、次の手順に従います。
ダウンロードしたファイルを展開し、Visual Studio を起動します。
メニュー バーで、[ ファイル]、[ 開く]、[ プロジェクト/ソリューション] の順に選択します。
[ プロジェクトを開く ] ダイアログ ボックスで、展開したサンプル コードを保持するフォルダーを開き、AsyncFineTuningVB のソリューション (.sln) ファイルを開きます。
ソリューション エクスプローラーで、ProcessTasksAsTheyFinish プロジェクトのショートカット メニューを開き、[スタートアップ プロジェクトとして設定] を選択します。
F5 キーを押してプロジェクトを実行します。
Ctrl キーを押しながら F5 キーを押して、プロジェクトをデバッグせずに実行します。
プロジェクトを複数回実行して、ダウンロードした長さが常に同じ順序で表示されないかどうかを確認します。
プロジェクトをダウンロードしない場合は、このトピックの最後にあるMainWindow.xaml.vb ファイルを確認できます。
例を構築する
この例では、「 1 が完了した後の残りの非同期タスクの取り消し (Visual Basic)」 で開発されたコードに追加し、同じ UI を使用します。
サンプルを自分でビルドするには、ステップ バイ ステップで、「サンプルのダウンロード」セクションの指示に従いますが、スタートアップ プロジェクトとして CancelAfterOneTask を選択します。 このトピックの変更を、そのプロジェクトの AccessTheWebAsync
メソッドに追加します。 変更はアスタリスクでマークされます。
CancelAfterOneTask プロジェクトには、実行時にタスクのコレクションを作成するクエリが既に含まれています。 次のコードでProcessURLAsync
を呼び出すたびに、TResult
が整数であるTask<TResult>が返されます。
Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
From url In urlList Select ProcessURLAsync(url, client, ct)
プロジェクトのMainWindow.xaml.vb ファイルで、 AccessTheWebAsync
メソッドに次の変更を加えます。
ToArrayではなく、Enumerable.ToListを適用してクエリを実行します。
Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
コレクション内の各タスクに対して次の手順を実行する while ループを追加します。
コレクション内の最初のタスクを識別してダウンロードを完了するための
WhenAny
の呼び出しを待機します。Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
そのタスクをコレクションから削除します。
downloadTasks.Remove(finishedTask)
ProcessURLAsync
の呼び出しによって返されるfinishedTask
を待機します。finishedTask
変数は、TReturn
が整数であるTask<TResult>です。 タスクは既に完了していますが、次の例に示すように、ダウンロードした Web サイトの長さを取得するまで待機します。Dim length = Await finishedTask resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
ダウンロードした長さが常に同じ順序で表示されないかどうかを確認するには、プロジェクトを複数回実行する必要があります。
注意事項
この例で説明されているように、 WhenAny
をループ内で使用して、少数のタスクに関連する問題を解決できます。 ただし、処理するタスクが多数ある場合は、他の方法の方が効率的です。 詳細と例については、「 完了時の処理タスク」を参照してください。
コード例全体
次のコードは、この例のMainWindow.xaml.vb ファイルの完全なテキストです。 アスタリスクは、この例で追加された要素を示します。
System.Net.Httpの参照を追加する必要があることに注意してください。
非同期サンプル: アプリケーションの微調整からプロジェクトをダウンロードできます。
' Add an Imports directive and a reference for System.Net.Http.
Imports System.Net.Http
' Add the following Imports directive for System.Threading.
Imports System.Threading
Class MainWindow
' Declare a System.Threading.CancellationTokenSource.
Dim cts As CancellationTokenSource
Private Async Sub startButton_Click(sender As Object, e As RoutedEventArgs)
' Instantiate the CancellationTokenSource.
cts = New CancellationTokenSource()
resultsTextBox.Clear()
Try
Await AccessTheWebAsync(cts.Token)
resultsTextBox.Text &= vbCrLf & "Downloads complete."
Catch ex As OperationCanceledException
resultsTextBox.Text &= vbCrLf & "Downloads canceled." & vbCrLf
Catch ex As Exception
resultsTextBox.Text &= vbCrLf & "Downloads failed." & vbCrLf
End Try
' Set the CancellationTokenSource to Nothing when the download is complete.
cts = Nothing
End Sub
' You can still include a Cancel button if you want to.
Private Sub cancelButton_Click(sender As Object, e As RoutedEventArgs)
If cts IsNot Nothing Then
cts.Cancel()
End If
End Sub
' Provide a parameter for the CancellationToken.
' Change the return type to Task because the method has no return statement.
Async Function AccessTheWebAsync(ct As CancellationToken) As Task
Dim client As HttpClient = New HttpClient()
' Call SetUpURLList to make a list of web addresses.
Dim urlList As List(Of String) = SetUpURLList()
' ***Create a query that, when executed, returns a collection of tasks.
Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
From url In urlList Select ProcessURLAsync(url, client, ct)
' ***Use ToList to execute the query and start the download tasks.
Dim downloadTasks As List(Of Task(Of Integer)) = downloadTasksQuery.ToList()
' ***Add a loop to process the tasks one at a time until none remain.
While downloadTasks.Count > 0
' ***Identify the first task that completes.
Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
' ***Remove the selected task from the list so that you don't
' process it more than once.
downloadTasks.Remove(finishedTask)
' ***Await the first completed task and display the results.
Dim length = Await finishedTask
resultsTextBox.Text &= String.Format(vbCrLf & "Length of the downloaded website: {0}" & vbCrLf, length)
End While
End Function
' Bundle the processing steps for a website into one async method.
Async Function ProcessURLAsync(url As String, client As HttpClient, ct As CancellationToken) As Task(Of Integer)
' GetAsync returns a Task(Of HttpResponseMessage).
Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)
' Retrieve the website contents from the HttpResponseMessage.
Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()
Return urlContents.Length
End Function
' Add a method that creates a list of web addresses.
Private Function SetUpURLList() As List(Of String)
Dim urls = New List(Of String) From
{
"https://msdn.microsoft.com",
"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
End Class
' Sample output:
' Length of the download: 226093
' Length of the download: 412588
' Length of the download: 175490
' Length of the download: 204890
' Length of the download: 158855
' Length of the download: 145790
' Length of the download: 44908
' Downloads complete.
こちらも参照ください
.NET