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

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

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

执行若干个任务,只需要对其中任意一个的完成进行响应。这主要用于:对一个操作进行多种独立的尝试,只要一个尝试完成,任务就算完成。例如,同时向多个Web 服务询问股票价格,但是只关心第一个响应的。

解决方案

使用Task.WhenAny 方法。该方法的参数是一批任务,当其中任意一个任务完成时就会返回。作为返回值的Task 对象,就是那个完成的任务。不要觉得迷惑,这个听起来有点难,但从代码看很容易实现:

// 返回第一个响应的URL 的数据长度。
private static async Task<int> FirstRespondingUrlAsync(string urlA, string urlB)
{
var httpClient = new HttpClient();

// 并发地开始两个下载任务。
Task<byte[]> downloadTaskA = httpClient.GetByteArrayAsync(urlA);
Task<byte[]> downloadTaskB = httpClient.GetByteArrayAsync(urlB);

// 等待任意一个任务完成。
Task<byte[]> completedTask =
await Task.WhenAny(downloadTaskA, downloadTaskB);

// 返回从URL 得到的数据的长度。
byte[] data = await completedTask;
return data.Length;
}

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

讨论

Task.WhenAny 返回的task 对象永远不会以“故障”或“已取消”状态作为结束。该方法的运行结果总是一个Task 首先完成。如果这个任务完成时有异常,这个异常也不会传递给Task.WhenAny 返回的Task 对象。因此,通常需要在Task 对象完成后继续使用await。

第一个任务完成后,考虑是否要取消剩下的任务。如果其他任务没有被取消,也没有被继续await,那它们就处于被遗弃的状态。被遗弃的任务会继续运行直到完成,它们的结果会被忽略,抛出的任何异常也会被忽略。

使用Task.WhenAny 可以实现超时功能(例如用Task.Delay 作为其中的一个任务),但这种做法并不可取。更常见的做法是采用专门有取消功能的超时函数,并且取消功能还有一个好处,就是可以把已经超时的任务彻底取消。

Task.WhenAny 的另一个反模式是处理已完成的任务。一种做法是把所有任务放在一个列表里,在一个任务完成后就把它移除,这种做法看起来好像有道理。问题是这种做法需要执行的时间是O(N^2),而实际上有时间复杂度为O(N) 的算法。时间复杂度为O(N) 的正确算法将在2.6 节介绍。

参阅

2.4 节介绍异步地等待所有任务完成。

2.6 节介绍等待一批任务完成,并对每个任务进行处理。

9.3 节介绍使用取消标志来实现超时功能。
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:2.4 等待一组任务完成
下一篇:2.6 任务完成时的处理
相关文章
图文推荐
排行
热门
最新书评
特别推荐

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

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