藉由使用 Task.WhenAny,您可以同時啟動多個工作,並在完成時逐一處理工作,而不是按照啟動的順序處理工作。
下列範例會使用查詢來建立工作集合。 每項工作都會下載指定網站的內容。 在 while 循環的每次迭代中,等候的呼叫 WhenAny 會回傳在工作集合中最先完成下載的工作。 該任務會從集合中移除並進行處理。 迴圈會重複執行,直到集合不再包含任何工作為止。
備註
若要執行範例,您必須在計算機上安裝Visual Studio 2012或更新版本和 .NET Framework 4.5 或更新版本。
下載範例
您可以從 異步範例:微調您的應用程式 ,然後遵循下列步驟,下載完整的 Windows Presentation Foundation (WPF) 專案。
解壓縮您下載的檔案,然後啟動 Visual Studio。
在功能表欄上,選擇 [ 檔案]、[ 開啟]、[ 專案/方案]。
在 [ 開啟專案 ] 對話框中,開啟保存解壓縮範例程式代碼的資料夾,然後開啟 AsyncFineTuningVB 的解決方案 (.sln) 檔案。
在 [方案總管] 中,開啟 ProcessTasksAsTheyFinish 專案的快捷方式功能表,然後選擇 [ 設定為啟始專案]。
選擇 F5 鍵以執行專案。
選擇 Ctrl+F5 鍵來執行專案,而不進行偵錯。
執行項目數次,以確認下載的長度不一定會以相同順序顯示。
如果您不想下載專案,您可以在本主題結尾檢閱MainWindow.xaml.vb檔案。
建構範例
本範例會在 One Is Complete (Visual Basic) 之後,新增至取消剩餘異步工作 中所開發的程式代碼,並使用相同的 UI。
若要自行建置範例,請逐步遵循「下載範例」一節中的指示,但選擇 CancelAfterOneTask 作為 StartUp 專案。 將本主題中的變更添加到該專案中的AccessTheWebAsync方法。 這些變更會以星號標示。
CancelAfterOneTask 專案已經包含執行時建立工作集合的查詢。 下列程式代碼中對 的每個呼叫 ProcessURLAsync 都會 Task<TResult> 傳回 ,其中 TResult 是整數。
Dim downloadTasksQuery As IEnumerable(Of Task(Of Integer)) =
From url In urlList Select ProcessURLAsync(url, client, ct)
在專案的MainWindow.xaml.vb檔案中,對方法進行下列變更 AccessTheWebAsync 。
套用 Enumerable.ToList 而非 ToArray來執行查詢。
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)等待
finishedTask,這是呼叫ProcessURLAsync後傳回的結果。 變數finishedTask是 Task<TResult> 其中TReturn是整數。 工作已經完成,但您等候它擷取所下載網站的長度,如下列範例所示。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.