ContinueWhenAll 不等待所有任务完成

10

我在网上找到了一段代码,并稍微修改了一下来查看它的工作原理,但现在我遇到了一个问题,就是 ContinueWhenAll 不会等待所有任务完成:

List<Task> tasks = new List<Task>();
for (int i = 0; i < 20; i++)
{
    int j = i;
    var compute = Task.Factory.StartNew(() => results.Add(DoSomething(j)));
    tasks.Add(compute);
}

我正在使用这段代码将所有任务添加到列表中。 DoSomething 函数计算一些结果并将它们添加到 BlockingCollection 中。我还有另一个显示函数,它将从 BlockingCollection 中添加的所有结果写入控制台。

我已经使用了这段代码等待所有任务完成,但似乎它并没有等待它们,因为程序在启动后仅几毫秒就显示标准的 "按任意键继续" 消息。(应该需要 ~20 秒才能完成程序)

Task.Factory.ContinueWhenAll(tasks.ToArray(), result => results.CompleteAdding());

然而,如果我在程序末尾添加Task.WaitAll(consume),程序可以正常工作:

var consume = Task.Factory.StartNew(() => display(results));
//results = BlockingCollection that I mentioned 
据我所知,程序没有足够的时间来显示所有来自BlockingCollection的结果,但在等待所有任务完成时,它仍然有足够的时间来显示其中一些结果。请问有人可以解释一下为什么Task.Factory.ContinueWhenAll不会等待所有结果被计算完毕,而程序就像没有那行代码一样迅速结束(几毫秒后)吗?
1个回答

20
Task.Factory.ContinueWhenAll 不是一个阻塞方法;它实际上会启动一个新任务,只有在所有提供的任务执行完成后才能正常工作。因此,在程序启动几毫秒后看到消息是正常的,因为它不会在您的主程序中 阻塞 等待任务完成。 来自Microsoft Learn

创建一个继续任务,当一组指定任务完成时启动该任务。

Task.WaitAll 在调用者处 阻塞,等待所有提供的任务执行完成。

“在完成一组提供的任务后”,这正是我所说的,它不应该创建新的任务,直到列表中的所有任务都完成了工作。唯一令人困惑的是程序是否会“在那一行”等待,还是在所有任务完成后进入下一行并启动新任务? - Dan
@Dan:当使用ContinueWhenAll时,程序不会在那一行“等待”,而是会继续执行下一行,并同时创建一个新的任务,该任务将不会执行,直到提供的任务完成。但是,WaitAll会在那一行“阻塞”,直到所有提供的任务完成它们的执行。 - Jalal Said
看起来这并不是完全正确的,你可以查看他的链接http://blogs.msdn.com/b/csharpfaq/archive/2010/06/18/parallel-programming-task-schedulers-and-synchronization-context.aspx?PageIndex=2,并且你会发现其中有一部分说我已经使用了ContinueWhenAll方法,它可以等待一组任务完成。 - Dan
这个问题应该有其他的解释。 - Dan
4
@Dan:ContinueWhenAll方法将安排一个新的任务,在第一组任务完成后运行。它不会在调用ContinueWhenAll的线程中导致任何等待。当该博客说它等待任务数组完成时,它仅意味着新任务等待其他任务完成。有时候,您应该尝试阅读官方文档,它说:“创建一个继续任务,该任务将在提供的一组任务完成后启动”。而不是依赖博客。 - Ben Voigt
是的,在使用ContinueWith而不是ContinueWhenAll进行一些测试后,我意识到你们两个都是正确的,抱歉。 - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接