完了後の残りの非同期タスクのキャンセル (Visual Basic)
Task.WhenAny メソッドを CancellationToken と共に使用すると、1 つのタスクが完了したときに残りのすべてのタスクを取り消しできます。 WhenAny
メソッドは、タスクのコレクションである引数を受け取ります。 このメソッドは、すべてのタスクを開始し、単一のタスクを返します。 単一のタスクは、コレクションのいずれかのタスクが完了すると完了します。
この例では、キャンセル トークンを WhenAny
と共に使用して、タスクのコレクションから最初のタスクを終了まで保持し、残りのタスクを取り消す方法を示しています。 各タスクは、Web サイトのコンテンツをダウンロードします。 この例は最初のダウンロードが完了したコンテンツの長さを表示し、他のダウンロードを取り消します。
注意
この例を実行するには、コンピューターに Visual Studio 2012 以降および .NET Framework 4.5 以降がインストールされている必要があります。
例をダウンロードする
完全な Windows Presentation Foundation (WPF) プロジェクトは、「Async Sample: Fine Tuning Your Application」(非同期のサンプル: アプリケーションの微調整) からダウンロードできます。その後、次の手順に従います。
ダウンロードしたファイルを圧縮解除し、Visual Studio を起動します。
メニュー バーで [ファイル] 、 [開く] 、 [プロジェクト/ソリューション] の順に選択します。
[プロジェクトを開く] ダイアログ ボックスで、圧縮解除したサンプル コードを含むフォルダーを開き、AsyncFineTuningVB 用のソリューション (.sln) ファイルを開きます。
ソリューション エクスプローラーで、CancelAfterOneTask プロジェクトのショートカット メニューを開き、 [スタートアップ プロジェクトに設定] をクリックします。
F5 キーを押してプロジェクトを実行します。
Ctrl + F5 キーを押して、デバッグを行わずにプロジェクトを実行します。
プログラムを複数回実行し、最初に終了するダウンロードが異なることを確認します。
プロジェクトをダウンロードしない場合は、このトピックの最後の MainWindow.xaml.vb ファイルをレビューできます。
例のビルド
このトピックの例では、「非同期タスクまたはタスクの一覧のキャンセル」で開発したプロジェクトに追加して、タスクのリストをキャンセルします。 この例では、 [キャンセル] ボタンは明示的に使用していませんが、同じ UI を使用します。
この例を自分で 1 つずつビルドするには、"例をダウンロードする" セクションの手順に従います。ただし、 [スタートアップ プロジェクト] として CancelAListOfTasks を選択します。 そのプロジェクトに、このトピックでの変更を追加します。
CancelAListOfTasks プロジェクトの MainWindow.xaml.vb ファイルで、各 Web サイトの処理ステップを AccessTheWebAsync
のループから次の非同期メソッドに移動して、遷移を開始します。
' ***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
この例では、AccessTheWebAsync
で、ToArray メソッドのクエリと WhenAny
メソッドを使用して、タスクの配列を作成して開始します。 配列への WhenAny
のアプリケーションは、待機したときに、タスクの配列で完了に到達する最初のタスクを評価する 1 つのタスクを返します。
AccessTheWebAsync
で次の変更を行います。 アスタリスクはコード ファイルの変更点を示しています。
ループをコメント アウトするか、削除します。
実行されると、一般的なタスクのコレクションを生成するクエリを作成します。
ProcessURLAsync
に対する各呼び出しは、Task<TResult> が整数であるTResult
を返します。' ***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)
ToArray
を呼び出してクエリを実行し、タスクを開始します。 次の手順でWhenAny
メソッドのアプリケーションは、ToArray
を使用せずにクエリを実行してタスクを開始しますが、他のメソッドはそうでない場合があります。 最も安全な方法は、クエリの実行を明示的に強制することです。' ***Use ToArray to execute the query and start the download tasks. Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
タスクのコレクションで
WhenAny
を呼び出します。WhenAny
はTask(Of Task(Of Integer))
またはTask<Task<int>>
を返します。 つまり、WhenAny
は、待機すると、単一のTask(Of Integer)
またはTask<int>
に評価するタスクを返します。 その単一のタスクが、コレクションで最初に終了するタスクです。 最初に終了したタスクはfinishedTask
に割り当てられます。finishedTask
の型は、Task<TResult> が整数であるTResult
です。それはProcessURLAsync
の戻り値の型であるためです。' ***Call WhenAny and then await the result. The task that finishes ' first is assigned to finishedTask. Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
この例では、最初に終了したタスクにのみ焦点を当てています。 したがって、CancellationTokenSource.Cancel を使用して、残りのタスクを取り消します。
' ***Cancel the rest of the downloads. You just want the first one. cts.Cancel()
最後に、
finishedTask
を待機して、ダウンロードされたコンテンツの長さを取得します。Dim length = Await finishedTask resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
プログラムを複数回実行し、最初に終了するダウンロードが異なることを確認します。
コード例全体
次のコードは、この例での MainWindow.xaml.vb または MainWindow.xaml.cs ファイルの全体です。 アスタリスクはこの例のために追加された要素を示しています。
System.Net.Http の参照を追加する必要があることに注意してください。
プロジェクトは、「Async Sample: Fine Tuning Your Application」(非同期のサンプル: アプリケーションの微調整) からダウンロードできます。
' 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 & "Download complete."
Catch ex As OperationCanceledException
resultsTextBox.Text &= vbCrLf & "Download canceled." & vbCrLf
Catch ex As Exception
resultsTextBox.Text &= vbCrLf & "Download 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()
'' Comment out or delete the loop.
''For Each url In urlList
'' ' GetAsync returns a Task(Of HttpResponseMessage).
'' ' Argument ct carries the message if the Cancel button is chosen.
'' ' Note that the Cancel button can cancel all remaining downloads.
'' Dim response As HttpResponseMessage = Await client.GetAsync(url, ct)
'' ' Retrieve the website contents from the HttpResponseMessage.
'' Dim urlContents As Byte() = Await response.Content.ReadAsByteArrayAsync()
'' resultsTextBox.Text &=
'' vbCrLf & $"Length of the downloaded string: {urlContents.Length}." & vbCrLf
''Next
' ***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 ToArray to execute the query and start the download tasks.
Dim downloadTasks As Task(Of Integer)() = downloadTasksQuery.ToArray()
' ***Call WhenAny and then await the result. The task that finishes
' first is assigned to finishedTask.
Dim finishedTask As Task(Of Integer) = Await Task.WhenAny(downloadTasks)
' ***Cancel the rest of the downloads. You just want the first one.
cts.Cancel()
' ***Await the first completed task and display the results
' Run the program several times to demonstrate that different
' websites can finish first.
Dim length = Await finishedTask
resultsTextBox.Text &= vbCrLf & $"Length of the downloaded website: {length}" & vbCrLf
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 downloaded website: 158856
' Download complete.
関連項目
.NET