【发布时间】:2018-05-21 17:26:55
【问题描述】:
Task.WhenAll(IEnumerable<Task>) 等待 IEnumerable 中的所有任务都完成 --- 但只有第一次调用时列表中的任务。如果有任何活动任务添加到列表中,则不会考虑它们。这个简短的例子展示了:
List<Task> _tasks = new List<Task>();
public async Task QuickExample()
{
for(int n =0; n < 6; ++n)
_tasks.Add(Func1(n));
await Task.WhenAll(_tasks);
Console.WriteLine("Some Tasks complete");
await Task.WhenAll(_tasks);
Console.WriteLine("All Tasks complete");
}
async Task Func1(int n)
{
Console.WriteLine($"Func1-{n} started");
await Task.Delay(2000);
if ((n % 3) == 1)
_tasks.Add(Func2(n));
Console.WriteLine($"Func1-{n} complete");
}
async Task Func2(int n)
{
Console.WriteLine($"Func2-{n} started");
await Task.Delay(2000);
Console.WriteLine($"Func2-{n} complete");
}
这个输出:
Func1-0 started
Func1-1 started
Func1-2 started
Func1-3 started
Func1-4 started
Func1-5 started
Func1-5 complete
Func1-3 complete
Func2-1 started
Func1-1 complete
Func1-0 complete
Func1-2 complete
Func2-4 started
Func1-4 complete
Some Tasks complete
Func2-4 complete
Func2-1 complete
All Tasks complete
Done
第二个Task.WhenAll() 解决了这种情况下的问题,但这是一个相当脆弱的解决方案。在一般情况下,处理此问题的最佳方法是什么?
【问题讨论】:
-
假设另一个线程将任务添加到
_tasks。 Task.WhenAll 应该如何知道添加完成? -
您可以尝试使您调用的方法更具“功能性”。该方法可以返回它在内部产生的任务集合。这样,您就可以保持代码干净且易于推理。恕我直言,其他可能的解决方案是“hacky”。
-
您只需要
WaitAll已知任务列表。如果您想添加它并继续等待,那么我强烈建议您采用不同的方法。这在许多方面都是有害的,尤其是如果您不是唯一的开发人员。如果你有一个线程添加任务然后等待那个线程然后WaitAll其余的当它完成添加它们时。只是一个建议而不是解决方案。 -
您可以简单地从生成它们的任务中
await新任务,而无需将它们级联到_tasks集合。如果 A 创建 B,则 A 在 B 完成之前不会完成。 -
您是否考虑过为此使用 Microsoft 的响应式框架?它更适合这种事情。
标签: c# .net task task-parallel-library