本篇體驗如何等待所有異步任務完成、等待任意一個異步任務完成,以及異步任務完成時的處理。
等待一組任務的完成使用Task.WhenAll方法。
Task task1 = Task.Delay(TimeSpan.FromSeconds(1));Task task2 = Task.Delay(TimeSpan.FromSeconds(2));Task task3 = Task.Delay(TimeSpan.FromSeconds(3));await Task.WhenAll(task1, task2, task3);
如果所有任務的結果類型相同且全部完成,Task.WhenAll返回每個任務執行結果的數組。
Task task1 = Task.FromResult(1);Task task2 = Task.FromResult(2);Task task3 = Task.FromResult(3);int[] results = await Task.WhenAll(task1, task2, task3);foreach(var item in results){Console.WriteLine(item);}
舉個例子,提供一個url的集合,要求根據這個url集合去遠程下載對應的內容,寫一個方法。
static async Task<string> DownloadAllAsync(IEnumerable<string> urls){var httpClient = new HttpClient();//定義每一個ulr的使用方法var downloads = urls.Select(url => httpClient.GetStringAsync(url));//下載真正開始Task<string>[] downloadTasks = downloads.ToArray();//異步等待string[] hmtls = await Task.WhenAll(downloadTasks);return string.Concat(htmls);}
如果在等待所有任務完成的過程中有異常發生怎麼辦呢?
如果想在等待過程中捕獲異常,那麼應該把WhenAll方法放在try語句塊中;如果想在所有任務完成後捕獲異常,那就應該把WhenAll方法返回的Task類型放在try語句塊中。
先模擬兩個異步異常。
static async Task ThrowNotImplementedExceptionAsync(){throw new NotImplementedException();}static async Task ThrowInvalidOperationExceptionAsync(){throw new InvalidOperationException();}
首先來看等待結果出來時的異常處理。
stati async Task ObserveOneExceptionAsync(){var task1 = ThrowNotImplementedExceptionAsync();var task2 = ThrwoInvalidOperationExceptionAsync();try{await Task.WhenAll(task1, ask2);}cach(Exception ex){}}
再來看等所有結果出來後的異常處理。
static async Task ObserveAllExceptionAsync(){var task1 = ThrowNotImplementedExceptionAsync();var task2 = ThrwoInvalidOperationExceptionAsync();Task allTasks = Task.WhenAll(task1, task2);try{await allTasks;}catch(Eexception ex){}}
等待任意一個任務的完成使用WhenAny方法。
比如有2個任務,通過2個url獲取異步遠程內容。
private static async Task<int> DownloadAsync(string url1, url2){var httpClient = new HttpClient();Task<byte[]> download1 = httpClient.GetByteArrayAsync(url1);Task<byte[]> download2 = httpClient.GetByteArrayAsync(url2);//等待任意一個任務完成Task<byte[]> completedTask = await Task.WhenAny(download1, download2);byte[] data = await completedTask;return data.Length;}
任務完成時如何處理呢?
思路有2個,一個是根據我們安排的順序出結果,還有一個是根據任務本身出結果的先後順序自然輸出結果。
首先來一個異步方法。
static async Task<int> DelayAsync(int val){await Task.Delay(TimeSpan.FromSeconds(val));return val;}
再寫一個手動部署任務順序的方法。
static async Task ProcessTasksAsync(){//創建任務隊列Task<int> task1 = DelayAsync(2);Task<int> task2 = DelayAsync(3);Task<int> task3 = DelayAsync(1);//手動安排任務的順序var tasks = new[]{task1, task2, task3};//按順序遍歷任務列表,逐一輸出結果foreach(var task in tasks){var result = await task;Console.Write(result);}}
輸出結果為231,是根據我們手動安排任務的順序輸出結果的。
如果我們想輸出123呢?即按照任務的不同讓結果自然發生。
思路是:以異步的方式處理輸出結果。
可以寫一個針對每個任務的異步方法。
static async Task AwaitAndProessAync(Task<int> task){var result = await task;Console.Write(result);}
現在修改ProcessTasksAsync方法如下:
static async Task ProcessTasksAsync(){//創建任務隊列Task<int> task1 = DelayAsync(2);Task<int> task2 = DelayAsync(3);Task<int> task3 = DelayAsync(1);//手動安排任務的順序var tasks = new[]{task1, task2, task3};var processingTasks = (from t in tasksselect AwaitAndProessAync(t)).ToArray();await Task.WhenAll(processingTasks);}
當然,也可以這樣修改ProcessTasksAsync方法。
static async Task ProcessTasksAsync(){//創建任務隊列Task<int> task1 = DelayAsync(2);Task<int> task2 = DelayAsync(3);Task<int> task3 = DelayAsync(1);//手動安排任務的順序var tasks = new[]{task1, task2, task3};var processingTasks = tasks.Select( async t => {var result = await t;Console.Write(result);}).ToArray();await Task.WhenAll(processingTasks);}
參考資料:C#並發編程經典實例