解決方案
Task.WhenAll
傳入若干任務,當所有任務完成時,返回一個完成的任務。
重載方法
Task WhenAll(IEnumerable<Task>)
Task WhenAll(params Task[])
Task<TResult[]> WhenAll<TResult>(IEnumerable<Task<TResult>>)
Task<TResult[]> WhenAll<TResult>(params Task<TResult>[])
舉例:
var task1 = Task.Delay(TimeSpan.FromSeconds(1));
var task2 = Task.Delay(TimeSpan.FromSeconds(2));
var task3 = Task.Delay(TimeSpan.FromSeconds(3));
await Task.WhenAll(task1, task2, task3);
當任務返回結果類型相同,所有任務完成返回的是,存着每個任務執行結果的數組。
var task1 = Task.FromResult(1);
var task2 = Task.FromResult(2);
var task3 = Task.FromResult(3);
int[] array = await Task.WhenAll(task1, task2, task3); //{1,2,3}
返回的數組中結果的順序,並非可控,如上述例子中,只是結果爲包含了1,2,3的數組,順序是不定的。
書中不建議使用以 IEnumerable 類型作爲參數的重載。文中沒有介紹作者不建議的原因。我找到作者個人博客的一篇文中中提到如下文字(文章地址:https://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html)
該段文字解釋了作者更喜歡使用LINQ結合ToArray的方式使用異步,因爲作者認爲代碼會更清晰。書中有例子,如下所示:
static async Task<string> DownloadAllAsync(IEnumerable<string> urls)
{
var httpClient = new HttpClient();
// 定義每一個 url 的使用方法。
var downloads = urls.Select(url => httpClient.GetStringAsync(url));
// 注意,到這裏,序列還沒有求值,所以所有任務都還沒真正啓動。
// 下面,所有的 URL 下載同步開始。
Task<string>[] downloadTasks = downloads.ToArray();
// 到這裏,所有的任務已經開始執行了。
// 用異步方式等待所有下載完成。
string[] htmlPages = await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
如果報錯記得添加如下引用
using System.Linq;
using System.Net.Http;
返回的Task的狀態
var task1 = ......;
var task2 = ......;
var task3 = ......;
Task allTasks = Task.WhenAll(task1, task2, task3);
以上述僞代碼爲例說明allTasks的狀態
- 當task1出現異常,異常會拋給allTasks,allTasks的狀態同task1狀態,也是Faulted。
- 當task1被取消,allTasks的狀態是Canceled
- 當task1, task2, task3,不出現異常,也未被取消,allTasks的狀態是RanToCompletion
Task.WhenAll的異常處理
上面提到了異常處理,當一個task異常,該異常會被allTasks接收,當多個task異常,這些異常也都會被allTasks接收。但是task1拋異常,task2也出異常,但是try catch 處理
拋出異常的方法
await Task.WhenAll(task1, task2);
只能抓取其中某一個異常。如何獲取所有異常呢?書中列舉了兩種處理方法,代碼如下拋出異常的方法
static async Task ThrowNotImplementedExceptionAsync()
{
throw new NotImplementedException();
}
static async Task ThrowInvalidOperationExceptionAsync()
{
throw new InvalidOperationException();
}
第一種處理方式,只能獲取其中一個異常
static async Task ObserveOneExceptionAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
try
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// ex 要麼是 NotImplementedException,要麼是 InvalidOperationException
//...
}
}
第二種處理方式,可以獲取所有異常
static async Task ObserveAllExceptionsAsync()
{
var task1 = ThrowNotImplementedExceptionAsync();
var task2 = ThrowInvalidOperationExceptionAsync();
Task allTasks = Task.WhenAll(task1, task2);
try
{
await allTasks;
}
catch
{
AggregateException allExceptions = allTasks.Exception;
//...
}
}
兩種方式的區別是,await調用
Task.WhenAll
返回的Task對象,即例子中的allTasks,代碼await allTasks;
作者在書中將對Task.WhenAll的異常處理放在了討論中,並說明了自己的處理方式
顯然作者更中意第一種方式。那麼你呢?
from : https://www.twblogs.net/a/5c2610d7bd9eee16b3db8997
沒有留言:
張貼留言