C#异步编程疑问:await是将控制权交还给调用方还是挂起线程?为何顶层调用需要使用await?
关于C#异步编程中await的核心疑问解答
嘿,我来帮你把这个异步编程的疑惑拆解清楚,结合你给的代码例子一一说明:
核心问题1:await操作是将控制权交还给调用方,还是挂起当前线程?
答案很明确:await是将控制权交还给调用方,而不是挂起当前线程。
具体到你的代码里:
当SomeMethod执行到await Task.Delay(10000)时,因为Task.Delay返回的是一个未完成的Task,编译器会做这几件事:
- 把
SomeMethod中await之后的代码(也就是打印Some Method End的部分)打包成一个回调函数 - 立即将控制权交还给调用它的
Main方法,当前线程不会被阻塞,而是会回到线程池(如果是线程池线程的话)去处理其他任务 - 等10秒的延迟任务完成后,.NET会从线程池里找一个空闲线程(或者根据同步上下文决定用哪个线程)来执行之前打包的回调,也就是继续完成
SomeMethod剩下的代码
你当前代码里看到Main Method End立刻被打印,就是因为await把控制权还给了Main,让Main可以继续往下执行,而不是等着SomeMethod完成。
核心问题2:为什么顶层调用需要使用await关键字?
这和你当前代码里的一个关键问题有关:你把SomeMethod定义成了async void。
async void是一种特殊的异步方法签名,几乎只应该用在事件处理程序里,原因有两个:
async void的方法不会返回Task,调用方无法跟踪它的执行状态,也没办法等待它完成async void方法里抛出的异常无法被调用方捕获,会直接崩溃进程
在你的代码里,Main调用SomeMethod()的时候,因为它是async void,Main根本不知道这个方法里有异步操作需要等待,所以会直接继续执行后面的Console.WriteLine("Main Method End")。
如果要让Main等待SomeMethod完成,你需要做两个修改:
- 把
SomeMethod的返回类型从async void改成async Task - 把
Main改成async Task Main(C# 7.1及以上版本支持控制台程序的异步Main方法),然后用await SomeMethod()来调用它
修改后的示例代码
using System; using System.Threading.Tasks; namespace AsynchronousProgramming { class Program { // 改成async Task Main,支持异步等待 static async Task Main(string[] args) { Console.WriteLine("Main Method Started......"); // 使用await等待SomeMethod完成 await SomeMethod(); Console.WriteLine("Main Method End"); Console.ReadKey(); } // 把async void改成async Task,让调用方能跟踪任务状态 public async static Task SomeMethod() { Console.WriteLine("Some Method Started......"); await Task.Delay(TimeSpan.FromSeconds(10)); Console.WriteLine("\n"); Console.WriteLine("Some Method End"); } } }
修改后,程序的执行流程就符合你的预期了:
- 打印
Main Method Started...... - 进入
SomeMethod打印Some Method Started...... - 执行
await Task.Delay(10000),把控制权交还给Main,但因为Main用了await,所以Main会暂停执行后续代码,等待SomeMethod的Task完成 - 10秒后,
SomeMethod继续执行,打印Some Method End SomeMethod的Task完成,Main恢复执行,打印Main Method End
额外补充:async void的风险
再次强调,除非你在写事件处理程序(比如WinForms/WPF的按钮点击事件),否则永远不要用async void。它会让你的异步代码失去可追踪性,并且隐藏异常,导致调试困难。
内容的提问来源于stack exchange,提问作者variable
相关产品推荐
相关产品推荐

