【问题标题】:Why Multiple await take same time like Task.WhenAll()为什么多个等待需要像 Task.WhenAll() 一样的时间
【发布时间】:2017-10-24 23:35:20
【问题描述】:

我了解当您有任务列表时,建议使用await Task.WhenAll() 而非多个await,因为Task.WhenAll() 处理异常的方式。但是,根据我对“async,await”工作方式的理解,我想知道为什么下面的代码块具有相同的执行时间:

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);
}

这个block需要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 的列表将按顺序执行,因为这是异步的,等待微软在 MSDN 中解释。我在这里缺少什么?是不是对编译器进行了优化?有人可以解释一下引擎盖下发生了什么吗?

【问题讨论】:

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


    【解决方案1】:

    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 在 5 秒后出现,Second finished 在 2 秒后出现,Third finished 在 3 秒后出现。

    要获得所需的结果,您必须按顺序调用函数并在此处调用 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- 在原始代码中,我尝试使用Console.WriteLine(firstTask.Status) 打印任务状态以查看它是否正在运行,我得到:“WaitingForActivation”,我认为它应该是“正在运行”,为什么? 2-我想我可能会保持MainAsync方法中的代码不变,并将SleepForTime(int seconds)方法更改为:public static Task SleepForTime(int seconds) { return new Task(async () => { await Task.Delay(10000); }); }但是MainAsync方法中的代码在任何方法中似乎都被阻止了,它没有根本没有完成?有什么想法吗?
    【解决方案2】:

    根据我的理解,第一个块应该需要 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);
    

    直到前一个任务完成后才会开始下一个任务。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-08-14
      • 1970-01-01
      • 2020-09-12
      • 2023-03-26
      • 2016-02-16
      • 2021-10-04
      • 2022-07-12
      • 2011-12-07
      相关资源
      最近更新 更多