2012-09-26 39 views
3

我需要对Web服务进行一系列调用以获取一组节点计数。我并行地并行进行所有呼叫。之后,我开始了电话,我总结的结果,像这样:以任何顺序等待所有任务的短路

//pendingTasks is List<Task<int>> 
int sum = 0; 
foreach (var task in pendingTasks) 
{ 
    sum += await task; 
    if (sum > 100) break; 
} 

的突破是有,因为它超过100

首先关闭后,我并不关心具体的数量,是危险的像这样打破循环?离开未完成任务不好吗?它会造成任何类型的内存泄漏吗?

其次,个别呼叫相当不一致。如果第一次打电话的时间最长,我会讨厌它,即使所有后面的打电话总数超过100次,我也会等到最后一次打电话。如果单个结果返回,那么最好加上总和。为了收到他们的订单。我之前使用过WhenAll,而且我很确定WhenAny是我想要的,但我不太清楚如何在这种场景中使用它,我想在它们进来时处理多个场景,然后在它们结束时终止全做完了。

回答

4

首先,打破这样的循环是危险的吗?离开未完成任务不好吗?它会造成任何类型的内存泄漏吗?

它不会创建内存泄漏,但是如果任务具有与它们关联的取消令牌,那么取消它们以避免不必要地执行额外的工作将会很有用。

在.NET 4中,其中有故障,但没有“发现”将在默认情况下(故意)打倒一个过程的缺陷,但是这已经放宽了对.NET 4.5的任务。

我用WhenAll之前,我敢肯定WhenAny是我想要的,但我不太知道如何在这种场景中,在这里我要处理多,因为他们进来使用,然后在全部完成时终止。

WhenAny肯定可以为你在这里工作,但另一种方法是“神奇”重新排序任务,使你可以遍历它们的顺序,他们完成。或者说,迭代新任务,其中以其完成顺序获得与原始任务相同的结果。

前一段时间我写了一个blog post on that very topic - 虽然这不是我的想法。基本上你创建了一堆TaskCompletionSource对象 - 每个原始任务都有一个对象 - 并为每个原始任务添加一个延续,以填充“下一个可用”任务完成源。

对于你如何使用WhenAny一个例子,你可以看看我的majority voting博客文章 - 但该缺点是有相当多的收集处理,并n调用WhenAny。 “神奇排序”创建工作的一个新的集合,但随后只是附加的延续,每个原始任务,所以没有什么真正地等待所有的人......你可以在他们遍历一次一个,等待每个一个轮流。

+0

我喜欢你的实现,而不是为TaskCompletionSources使用'List'并同步访问,为什么不使用'BlockingCollection'?然后,您可以在开始时添加它们,并在每次完成任务时选择一个。 – Servy

+1

+1。 Stephen Toub有[另一种方法](http://blogs.msdn.com/b/pfxteam/archive/2012/08/02/processing-tasks-as-they-complete.aspx)返回任务> [] ',这允许一些更高级的情况。但是我认为Jon的使用更容易。 –

1

Jon Skeet的回答和Stephen Toub的方法都很棒。

另一种方法是使用TPL DataFlow库中的BufferBlock<int>。如果你有机会访问编辑任务的参数,你可以简单地传递一个BufferBlockPost结果:

var buffer = new BufferBlock<int>(); 

//Run your tasks somehow like so: 
YourAsyncFunctionThatPostsAnInt(buffer, cancellationTokenSource.Token) 
... 

int sum = 0; 
while(sum < 100) 
{ 
    sum += await buffer.ReceiveAsync() 
} 

buffer.Complete(); 
cancellationTokenSource.Cancel(); 

即使与现有的任务,你可以为他们自己的结果添加延续到Post到缓冲区。取消令牌是缩短执行的最佳方式。