为什么Multiple await需要像Task.WhenAll()一样的时间

时间:2017-10-24 23:35:21

标签: c# asynchronous async-await task console-application

据我所知,当您有一个任务列表时,由于await Task.WhenAll()处理异常的方式,建议您使用await多个Task.WhenAll()。但是,从我对#as; async的方式的理解,等待"工作,我想知道为什么下面的代码块具有相同的执行时间:

static void Main(string[] args)
{
    MainAsync(args).GetAwaiter().GetResult();
    Console.ReadLine();
}

static async Task MainAsync(string[] args)
{
    Console.WriteLine("Starts :" + DateTime.Now.ToLongTimeString());
    var firstTask = SleepForTime(10000);
    var secondTask = SleepForTime(7000);
    var thirdTask = SleepForTime(5000);

    await firstTask;
    await secondTask;
    await thirdTask;

    Console.WriteLine("Done :" + DateTime.Now.ToLongTimeString());

    Console.ReadLine();
}

public static async Task SleepForTime(int seconds)
{
    await Task.Delay(seconds);
}

此块将需要10秒钟才能执行,这与此相同:

static void Main(string[] args)
{
    MainAsync(args).GetAwaiter().GetResult();
    Console.ReadLine();
}

static async Task MainAsync(string[] args)
{
    Console.WriteLine("Starts :" + DateTime.Now.ToLongTimeString());
    var firstTask = SleepForTime(10000);
    var secondTask = SleepForTime(7000);
    var thirdTask = SleepForTime(5000);

    await Task.WhenAll(firstTask, secondTask, thirdTask);

    Console.WriteLine("Done :" + DateTime.Now.ToLongTimeString());

    Console.ReadLine();
}

public static async Task SleepForTime(int seconds)
{
    await Task.Delay(seconds);
}

根据我的理解,第一个块应该花费22秒,因为await列表将按顺序逐个执行,因为这是异步,等待Microsoft在MSDN中解释。我在这里失踪了什么?这是编译器优化的东西吗?有人可以解释一下这个问题吗?

2 个答案:

答案 0 :(得分:3)

Task.Delay在内部使用计时器通知程序应该何时继续。一旦拨打Task.Delay,计时器就会启动。在这两种情况下,当您将任务存储在变量中时,您会一个接一个地启动任务,以后只能await它们。在等待其中任何一个时,定时器仍然在后台,并且因为它们或多或少同时启动,它们在延迟时间最长的那个完成时完成

var firstTask = SleepForTime(10000);
var secondTask = SleepForTime(7000);
var thirdTask = SleepForTime(5000);  

// All of the tasks are already started
Console.WriteLine("Start");
await firstTask;                       //this finishes after ~10s
Console.WriteLine("First finished");
await secondTask;                      //this finishes immediately
Console.WriteLine("Second finished");
await thirdTask;                       //this also finishes immediately
Console.WriteLine("Third finished");

所有First/Second/Third finished消息几乎在10秒后同时出现。经过一些修改后,您可以看到更改:

var firstTask = SleepForTime(5000);
var secondTask = SleepForTime(7000);
var thirdTask = SleepForTime(10000);  

// All of the tasks are already started
Console.WriteLine("Start");
await firstTask;                      //this finishes after ~5s
Console.WriteLine("First finished");
await secondTask;                     //this finishes after 2 more seconds
Console.WriteLine("Second finished");
await thirdTask;                      //this finishes after 3 more seconds
Console.WriteLine("Third finished");

现在First finished显示在5s之后,Second finished显示在另外2s之后,Third finished显示在另外3s之后。

要获得所需的结果,您必须按顺序调用函数,然后await每个函数都在那里,如下所示:

Console.WriteLine("Start");
await SleepForTime(10000);            //this finishes after 10s
Console.WriteLine("First finished");
await SleepForTime(7000);             //this finishes after 7s
Console.WriteLine("Second finished");
await SleepForTime(5000);             //this finishes after 5s
Console.WriteLine("Third finished");

SleepForTime函数是某些样式改进的理想选择 - 使用后缀Async表示它应该在异步代码中使用,并且您可以返回从Task.Delay返回的任务本身使代码更简单(为您和编译器)

public static Task SleepForTimeAsync(int seconds)
{
    return Task.Delay(seconds);
}

答案 1 :(得分:1)

  

根据我的理解,第一个块应该花费22秒,因为await列表将逐个执行。

没有。在等待它们之前,您正在启动所有3个任务,因此它们都在同一时间运行。

var firstTask = SleepForTime(10000); //start the 1st Task
var secondTask = SleepForTime(7000); //start the 2nd Task
var thirdTask = SleepForTime(5000);  //start the 3rd Task

await firstTask;  //wait for the 1st Task to finish
await secondTask; //wait for the 2nd Task to finish
await thirdTask;  //wait for the 3rd Task to finish

以下需要22秒:

await SleepForTime(10000);
await SleepForTime(7000);
await SleepForTime(5000);

下一个任务在上一个任务完成之前不会启动。