频道栏目
读书频道 > 软件开发 > C# > C#并发编程经典实例
2.4 等待一组任务完成
2014-12-06 13:59:34     我来说两句
收藏   我要投稿

本文所属图书 > C#并发编程经典实例

本书全面讲解C 并发编程技术,侧重于 NET 平台上较新、较实用的方法。全书分为几大部分:首先介绍几种并发编程技术,包括异步编程、并行编程、TPL 数据流、响应式编程;然后阐述一些重要的知识点,包括测试技  立即去当当网订购
问题

执行几个任务,等待它们全部完成。

解决方案

框架提供的Task.WhenAll 方法可以实现这个功能。这个方法的输入为若干个任务,当所有任务都完成时,返回一个完成的Task 对象:

Task task1 = Task.Delay(TimeSpan.FromSeconds(1));
Task task2 = Task.Delay(TimeSpan.FromSeconds(2));
Task task3 = Task.Delay(TimeSpan.FromSeconds(1));
await Task.WhenAll(task1, task2, task3);

如果所有任务的结果类型相同,并且全部成功地完成,则Task.WhenAll 返回存有每个任务执行结果的数组:

Task task1 = Task.FromResult(3);
Task task2 = Task.FromResult(5);
Task task3 = Task.FromResult(7);
int[] results = await Task.WhenAll(task1, task2, task3);
// "results" 含有 { 3, 5, 7 }

Task.WhenAll 方法有以IEnumerable 类型作为参数的重载,但建议大家不要使用。只要异步代码与LINQ 结合,显式的“具体化”序列(即对序列求值,创建集合)就会使代码更清晰:

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);
}

如果使用Microsoft.Bcl.Async 这个NuGet 库,则WhenAll 是TaskEx 类的成员,而不是Task 类的成员。

讨论

如果有一个任务抛出异常,则Task.WhenAll 会出错,并把这个异常放在返回的Task 中。

如果多个任务抛出异常,则这些异常都会放在返回的Task 中。但是,如果这个Task 在被await 调用,就只会抛出其中的一个异常。如果要得到每个异常,可以检查Task.WhenALl返回的Task 的Exception 属性:

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;
...
}
}

使用Task.WhenAll 时,我一般不会检查所有的异常。通常情况下,只处理第一个错误就足够了,没必要处理全部错误。

参阅

2.5 节介绍等待一批任务中的任意一个完成的方法。

2.6 节介绍等待一批任务完成,并逐个处理完成的任务。

2.8 节介绍对async 方法的异常处理。
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.3 报告进度
下一篇:2.5 等待任意一个任务完成
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站