异步等待行为

HLL*_*Lau 3 c# async-await

我正在试验 async-await 并且我遇到了这种相当奇怪的行为,至少对我来说是这样。
我创建了三种模拟长时间运行任务的方法。
考虑两个按钮单击处理程序:
对于 button1_click,经过的时间约为 6000 毫秒,而 button2_click 约为 3000 毫秒。
我无法理解为什么会发生这种情况,即 6000 毫秒与 3000 毫秒。

    private async Task<string> TaskOne()
    {
        await Task.Delay(1000);
        return "task one";
    }

    private async Task<string> TaskTwo()
    {
        await Task.Delay(2000);
        return "task two";
    }

    private async Task<string> TaskThree()
    {
        await Task.Delay(3000);
        return "task three";
    }
    
    //time elapsed = 6000+ms
    private async void button1_Click(object sender, EventArgs e)
    {
        var watch = new Stopwatch();
        watch.Start();

        await TaskOne();
        await TaskTwo();
        await TaskThree();
        
        watch.Stop();
        textBox3.Text = watch.ElapsedMilliseconds.ToString();
    }

    //time elapsed = 3000+ms
    private async void button2_Click(object sender, EventArgs e)
    {
        var watch = new Stopwatch();
        watch.Start();

        var taskOne = TaskOne();
        var taskTwo = TaskTwo();
        var taskThree = TaskThree();

        await taskOne;
        await taskTwo;
        await taskThree;
        
        watch.Stop();
        textBox3.Text = watch.ElapsedMilliseconds.ToString();
    }
Run Code Online (Sandbox Code Playgroud)

小智 7

在这种情况下:

 await TaskOne();
 await TaskTwo();
 await TaskThree();
Run Code Online (Sandbox Code Playgroud)

TaskTwo() 在 TaskOne() 完成之前无法启动,因为您正在等待它。同样,由于等待,TaskThree() 在 TaskTwo() 完成之前无法启动。

下一个:

var taskOne = TaskOne();
var taskTwo = TaskTwo();
var taskThree = TaskThree();

await taskOne;
await taskTwo;
await taskThree;
Run Code Online (Sandbox Code Playgroud)

您同时开始所有 3 个任务,然后等待它们。所以这就是为什么它只需要与运行时间最长的任务一样长的时间。你会惊讶于有多少人不了解异步等待。如果任务不相互依赖,那么这就是要走的路。


AAA*_*ddd 7

概括

这里的要点(否则这是一个非常常见的误解),await实际上确实意味着“等待”


await 运算符(C# 参考)

强调我的

await 运算符暂停对封闭异步方法的计算,直到其操作数表示的异步操作完成。当异步操作完成时,await 运算符返回操作的结果(如果有)。

当 await 运算符应用于表示已完成操作的操作数时,它会立即返回操作结果,而不会暂停封闭方法

await 运算符不会阻塞评估异步方法的线程。当 await 运算符挂起封闭的异步方法时,控制权返回给方法的调用者。


所以这就是正在发生的事情。在您的第一个示例中,您正在启动每个任务并等待它们连续完成。也就是说,这就像要求某人去做某事并完成,然后再要求下一个人做某事,等等

await TaskOne();    // start, do something and wait for it
await TaskTwo();    // start, do something and wait for it
await TaskThree();  // start, do  something and wait for it
Run Code Online (Sandbox Code Playgroud)

你的第二个例子。您实际上是在启动 3 个任务(热),然后等待它们一次完成一个。也就是说它们同时运行,并按顺序等待。

也就是说,你要对 3 个朋友说,去做些事情,然后等第一个回来,然后是第二个,然后是第三个。它更有效率......在前一个回来之前,没有讨厌的朋友闲逛。

即使第二个任务一个任务之前完成,在查看第二个任务完成状态等之前,您实际上仍然在等待第一个任务

var taskOne = TaskOne();     // start, do something
var taskTwo = TaskTwo();     // start, do something
var taskThree = TaskThree(); // start, do something

// all 3 tasks are started

await taskOne;   // wait for it
await taskTwo;   // wait for it
await taskThree; // wait for it
Run Code Online (Sandbox Code Playgroud)

或者类似地你可以使用 Task.WhenAll

创建一个将在所有提供的任务完成后完成的任务。

var taskOne = TaskOne();     // start, do something
var taskTwo = TaskTwo();     // start, do something
var taskThree = TaskThree(); // start, do something

// wait for them all to finish!
await Task.WhenAll(taskOne, taskTwo, taskThree);
Run Code Online (Sandbox Code Playgroud)