我需要对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
是我想要的,但我不太确定如何在这种场景中使用它,我想在它们来时处理多个in,然后在完成所有操作后终止。
答案 0 :(得分:4)
首先,像这样摆脱循环是危险的吗?离开待处理的任务是不是很糟糕?它会造成任何类型的内存泄漏吗?
它不会造成内存泄漏,但如果任务具有与之关联的取消令牌,则取消它们以避免不必要地执行额外工作将非常有用。
在.NET 4中,出现故障但没有“观察到”这些错误的任务会默认(故意)关闭一个进程但是.NET 4.5已经放宽了。
我之前使用过WhenAll,我很确定WhenAny是我想要的,但是我不太确定如何在这种情况下使用它,我希望在它们进入时处理多个,然后终止当他们都完成了。
WhenAny
肯定可以在这里为你工作,但另一种方法是“神奇地”重新排序任务,以便你可以按照它们完成的顺序迭代它们。或者更确切地说,迭代新任务,这些任务按照原始任务的顺序得到相同的结果
我刚才写了一篇blog post on that very topic - 虽然这不是我的主意。基本上,您创建了一堆TaskCompletionSource
个对象 - 每个原始任务一个 - 并为每个原始任务添加一个延续,以填充“下一个可用”任务完成源。
有关如何使用WhenAny
的示例,您可以查看我的majority voting博客文章 - 但其缺点是需要进行大量的集合操作,n
致电WhenAny
。 “魔术重新排序”创建了一个新的任务集合,但随后只是为每个原始任务附加一个延续,所以没有什么真正等待所有这些任务......你可以一次迭代一个,等待每个一个又一个。
答案 1 :(得分:1)
Jon Skeet的回答以及与Stephen Toub的方法的联系都很棒。
另一种方法是使用TPL DataFlow库中的BufferBlock<int>
。如果您有权修改任务的参数,则只需传递BufferBlock
和Post
您的结果:
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
到缓冲区。取消令牌是短路执行的最佳方式。