'await' 什么时候立即离开一个方法,什么时候不?

Lor*_*eno 2 c# multithreading asynchronous task async-await

在使用 async-await 的程序中,我的理解是这样的:

  • 未等待的异步方法将在后台运行 (?),其余代码将在此非等待方法完成之前继续执行
  • IS 等待的异步方法将等待该方法完成,然后再转到下一行代码

下面的申请是我写的,以检查上述陈述是否正确。

using System;
using System.Threading.Tasks;

namespace ConsoleApp3
{
    class Program
    {
        static async Task Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            DoJob();

            var z = 3;

            Console.ReadLine();
        }

        static async Task DoJob()
        {
            var work1 = new WorkClass();
            var work2 = new WorkClass();

            while (true)
            {
                await work1.DoWork(500);
                await work2.DoWork(1500);
            }
        }
    }

    public class WorkClass
    {
        public async Task DoWork(int delayMs)
        {
            var x = 1;

            await Task.Delay(delayMs);

            var y = 2;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

以下是我的一些观察:

  • DoJob();呼叫未等待。但是,调试器显示正在执行 DoJob 内部的代码,就好像它是一个普通的非异步方法一样。
  • 当代码执行到 时await work1.DoWork(500);,我会想“好吧,那么也许现在该DoJob方法将被留下并 var z = 3;会被执行?毕竟,'await' 应该离开该方法。” 实际上,它只是进入DoWork而不离开DoJob-var z = 3;仍然没有被执行。
  • 最后,当执行到达时await Task.Delay(delayMs);DoJob离开,var z = 3;到达。之后,Delay执行之后的代码。

我不明白的事情:

  • 为什么await Task.Delay(delayMs);离开DoJob方法,但await work1.DoWork(500);没有?
  • 我看到它DoJob正在正常执行。我认为它会在后台完成(也许由线程池线程之一?)。如果它是一些长时间运行的方法,它看起来可能会阻塞线程,对吗?

Pau*_*ado 5

编译器将async方法中的代码分成块。1 之前的第一个await和 1 之间的每个await和 1 之后的最后一个await

执行将在第一个未完成的等待者或方法结束时返回给调用者。

此方法仅Task在完全执行后才返回完成:

async Task M1() => await Task.CompletedTask;
Run Code Online (Sandbox Code Playgroud)

此方法将只返回一个不完整的Task,当Task返回的完成时将Task.Dealy(1000)完成:

async Task M2() => await Task.Delay(1000);
Run Code Online (Sandbox Code Playgroud)

这是一个小例子:

static async Task Main(string[] args)
{
    var t = TwoAwaits();
    Console.WriteLine("Execution returned to main");
    await t;
}
private static async Task TwoAwaits()
{
    Console.WriteLine("Before awaits");
    await Task.CompletedTask;
    Console.WriteLine("Between awaits #1");
    await Task.Delay(1000);
    Console.WriteLine("Between awaits #2");
    await Task.Delay(1000);
    Console.WriteLine("After awaits");
}
/*
Before awaits
Between awaits #1
Execution returned to main
Between awaits #2
After awaits
*/
Run Code Online (Sandbox Code Playgroud)


Ste*_*ary 5

为什么等待 Task.Delay(delayMs); 离开 DoJob 方法,但等待 work1.DoWork(500); 才不是?

因为这段代码:

await work1.DoWork(500);
Run Code Online (Sandbox Code Playgroud)

与此代码相同:

var task = work1.DoWork(500);
await task;
Run Code Online (Sandbox Code Playgroud)

所以,你的代码在调用方法第一,然后等待返回的任务。通常await所说的“等待方法调用”,但这并不是实际发生的情况——从技术上讲,首先(同步)完成方法调用,然后等待返回的任务。

我看到 DoJob 正在正常执行。我认为它会在后台完成(也许由线程池线程之一?)。

不; 对于真正的异步操作,没有线程被该操作阻塞。

如果它是一些长时间运行的方法,它看起来可能会阻塞线程,对吗?

是的。

我的理解是这样的

我建议阅读我的异步介绍以获得更好的心理框架。总之:

  1. async启用await关键字。它还生成一个状态机来处理创建Task返回值之类的事情。
  2. await对“可等待的”(通常是任务)进行操作。首先,它会检查它是否已经完成;如果是,该async方法将继续同步执行。
  3. 如果 awaitable 尚未完成,则await(默认情况下)捕获其上下文并安排该async方法的继续在等待完成时在该上下文上运行。