何时使用OrderByCompletion(Jon Skeet)和Parallel.ForEach与异步委托

最近,NDC伦敦的Jon Skeet谈到了C#5 async / await,并提出了“ 按完成排序 ”的想法,列出了异步任务。 链接http://msmvps.com/blogs/jon_skeet/archive/2012/01/16/eduasync-part-19-ordering-by-completion-ahead-of-time.aspx

我有点困惑,或者我应该说我不确定这种技术何时更适合使用。

我无法理解这个和下面的例子之间的区别

var bag = new ConcurrentBag(); Parallel.ForEach(myCollection, async item => { // some pre stuff var response = await GetData(item); bag.Add(response); // some post stuff } 

或由Stephen Toub解释的ForEachAsync – http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx

编辑:发现一篇来自Stephen Toub的博客文章解释“按完成排序”“处理完成后的任务”。 值得阅读。 阅读本文后,我可以清楚地了解它的工作原理以及何时使用这种技术。

  • 不要使用Parallel.ForEach来执行async代码。 Parallel.ForEach不理解async ,因此你的lambda将变成async void ,这将无法正常工作( Parallel.ForEach将在所有工作完成之前返回;exception将无法正确处理;可能还有其他问题)。

  • 当你有一个对象集合(而不是Task ForEachAsync()时,使用类似ForEachAsync()东西,你想为每个对象执行一些async操作,并且这些操作应该并行执行。

  • 当你有一个Task集合时,使用OrderByCompletion() ,你想为每个Task的结果执行一些操作(异步或非异步),这些操作不应该并行执行,你想根据顺序执行动作Task完成。

 Parallel.ForEach(myCollection, async item => 

这几乎肯定不是你想要的。 委托具有Action类型,因此匿名方法是async void方法。 这意味着它会被启动,除了通过检查其任何副作用之外,你无法检查其状态。 特别是,如果出现任何问题,您将无法捕获并处理exception。

但是,假设什么都没有出错,结果会在完成后添加到bag中。 在任何事情完成之前, bag将是空的。

相反, OrderByCompletion返回一个IEnumerable> ,它立即包含所有尚未完成的任务。 您可以await第五个元素,并在任何五个任务完成后继续。 例如,当您想要运行大量任务并定期更新表单以显示进度时,这可能很有用。

你给出的第三个选项, ForEachAsync ,就像ForEach一样,除非它做得正确,没有上面提到的问题。