等待任务和等待Task.WhenAll之间的区别

时间:2016-10-21 20:43:21

标签: c# asynchronous async-await task-parallel-library

在多个等待任务上使用await与等待完成所有任务时有什么区别。我的理解是方案2在性能方面更好,因为asyncTask1和asyncTask2都是并行执行的。

方案1:

async Task Task()
{
   await asyncTask1();
   await asyncTask2();
}

方案2:

async Task Task()
{
    t1 = asyncTask1();
    t2 =  asyncTask2();
    await Task.WhenAll(createWorkflowtask, getTaskWorkflowTask);
}

4 个答案:

答案 0 :(得分:6)

在方案1中,任务按顺序运行(asyncTask1必须在asyncTask2启动之前完成),而在方案2中,两个任务可以并行运行。

答案 1 :(得分:3)

在第一个场景中,你启动一个任务,然后等到完成,然后转到第二个,然后等到完成,然后退出方法。
在第二个场景中,您并行启动两个任务,然后等到调用Task.WhenAll时完成tgey

答案 2 :(得分:3)

您写道:" ...因为asyncTask1和asyncTask2都是并行执行的。"

不,不是!

  

补充:下面我写道,async-await中的所有内容都由一个线程执行。 Schneider正确地评论说,在async-await中可以涉及多个线程。见最后的补充。

一篇帮助我理解async-await如何工作的文章是this interview with Eric-Lippert谁将async / await与做饭的晚餐进行了比较。 (在某处中途,搜索async-await)。

Eric Lippert解释说,如果一个厨师开始做某事并在一段时间后发现他没有别的事情可以等待一个过程完成,那么这位厨师四处寻找他是否可以做其他事情而不是等待。 / p>

使用async / await时,仍然涉及一个线程。这个线程一次只能做一件事。当线程忙于执行Task1时,它无法执行Task2。只有在Task1中找到await时,它才会开始执行Task2中的语句。在您的方案2中,任务不会并行执行。

然而情景之间存在差异。在方案1中,task1的第一个语句将在task1完全完成之前不执行。一旦task1遇到等待,场景2将开始执行task2的第一个语句。

如果你真的希望task2在task1做某事时做某事,你必须在一个单独的线程中开始做task2。在您的方案中执行此操作的简单方法是:

var task1 = Task.Run( () => asyncTask1())
// this statement is executed while task1 begins executing on a different thread.
// hence this thread is free to do other things, like performing statements
// from task2:
var task2 = asyncTask();
// the following statement will only be executed if task2 encounters an await
DoSomethingElse();
// when we need results from both task1 and task2:
await Task.WhenAll(new Task[] {task1, task2});

通常,如果您需要此任务的结果,最好只等待任务完成。只要你可以做其他的事情,做其他的事情,一旦其他任务开始等待,它们将被执行,直到你开始等待,在这种情况下你的来电者将开始做事直到他等待等等。

这种方法在上面并行处理的优点有很多:

  • 一切都由一个线程执行:不需要互斥,没有死锁,饥饿等机会
  • 您的代码看起来很顺序。将其与使用Task.ContinueWith和类似语句的代码进行比较
  • 从线程池
  • 启动单独的线程/运行线程没有任何开销
  

另外:施耐德关于以下几条主题的评论是正确的

一些测试显示,等待任务中当前线程的线程ID与调用线程的线程ID不同。

对于异步等待的新手,重要的是要理解尽管涉及各种线程,但async-await并不意味着任务是并行执行的。如果你想要并行性,你必须要说任务必须并行运行。

Eric Lippert的厨师似乎实际上是一个厨师团队,他们不断环顾四周,看看他们是否可以帮助其他厨师,而不是等待他们的任务完成。事实上,如果阿尔伯特看到等待并开始做其他事情,伯纳德厨师可能会完成阿尔伯特厨师的任务。

答案 3 :(得分:1)

使用Task.WhenAll

  • 除非所有返回类型相同且使用通用版本的WhenAll方法
  • ,否则不能使用返回类型的方法
  • 您无法控制执行方法的序列,因为这些方法是并行执行的
  • 一种方法中未处理的异常不会破坏其他方法的执行(因为并行性)

来自MSDN

  

如果任何提供的任务在故障状态下完成,则返回的任务也将在TaskStatus.Faulted状态下完成,其中异常将包含来自每个提供的任务的一组未包装的异常的聚合。

在多种方法上使用await

  • 您可以控制名为

  • 的函数的序列
  • 您可以使用不同的返回类型,并在后续步骤中使用该返回类型

  • 一种方法中未处理的异常会破坏其他方法的执行