本篇体验如何等待所有异步任务完成、等待任意一个异步任务完成,以及异步任务完成时的处理。
等待一组任务的完成使用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#并发编程经典实例