没有await的嵌套异步方法块

本文关键字:异步方法 嵌套 await 没有 | 更新日期: 2023-09-27 18:17:12

我不确定这里发生了什么。据我所知,只要方法遇到await关键字,它就会有效地返回,然后在Task完成后继续执行。然而,这不是我观察到的行为。我有两个简单的方法:

public static async Task<bool> Level1()
{            
    var t = await Level2();      
    Console.WriteLine("L1 done.");  
    return t;                     
}
public static async Task<bool> Level2()
{
    int delay = 10;
    Thread.Sleep(100);
    var t = new TaskFactory();
    t.StartNew(() => { Thread.Sleep(delay); });
    Console.WriteLine("L2 done.");
    return true;
}
在main中,我调用
static void Main(string[] args)
{
    Level1();
    Console.WriteLine("Main done.");
}

现在,我希望Main()在调用Level1()之后继续执行,因为它没有等待,并且因为Level1()包含await关键字。然而,结果输出是

L2 done.
L1 done.
Main done.

指示我的异步方法正在同步运行。修改Level2()以使用await关键字解决了这个问题,并且Level1()调用不再阻塞。为什么我需要在嵌套方法Level2()方法中使用"await"来防止Level1()阻塞Main?

没有await的嵌套异步方法块

使用async关键字标记方法并不会使其成为异步方法。它只允许使用await(并将结果或异常包装在任务中)。

如果你有一个async方法,它内部不等待任何异步操作,那么这个方法是完全同步的,并将在调用线程上运行。

你可能忽略的是,使用await并不一定会使方法异步运行,而在未完成的任务上使用await则会。当任务已经完成时,没有理由挂起该方法。你可以从任务中获取结果,并保持同步运行。

因为Level2是同步运行的。当Level1准备好等待它的任务时,它已经完成了,所以Level2保持同步运行。请记住,您等待的是一个任务,而不是一个方法,因此您的代码实际上是这样运行的:

public static async Task<bool> Level1()
{            
    var $task = Level2(); // Runs synchronously
    var t = await $task; // Awaiting a completed task      
    Console.WriteLine("L1 done.");  
    return t;                     
}