await会将异步代码转为同步吗?关于async/await作用的技术疑问
我在学习async和await机制时,看到一段标注为异步代码的示例:
class Program { static async Task Main(string[] args) { Method2(); var count = await Method1(); Method3(count); Console.ReadKey(); } static async Task<int> Method1() { int count = 0; await Task.Run(() => { for (int i = 0; i < 3; i++) { Console.WriteLine(" Method 1"); count += 1; } }); return count; } static void Method2() { for (int i = 0; i < 3; i++) { Console.WriteLine(" Method 2"); } } static void Method3(int count) { Console.WriteLine("Total count is " + count); } }
我原本理解异步编程是允许两段独立代码同时运行的并发形式,但这段代码里的await让执行顺序固定,甚至可以用下面的同步代码实现相同输出:
class Program { static async Task Main(string[] args) { Method2(); Method3(Method1()); Console.ReadKey(); } static int Method1() { int count = 0; for (int i = 0; i < 3; i++) { Console.WriteLine(" Method 1"); count += 1; } return count; } static void Method2() { for (int i = 0; i < 3; i++) { Console.WriteLine(" Method 2"); } } static void Method3(int count) { Console.WriteLine("Total count is " + count); } }
因此我产生疑问:为何要使用await操作符?它是否本质上将异步代码转为了同步?
解答
await并没有把异步代码转为同步,它的核心作用是非阻塞地等待任务完成,同时释放当前线程去处理其他工作——这和同步代码里的阻塞等待有本质区别。
1. await的本质逻辑
当你await一个尚未完成的Task时,当前的异步方法会暂停执行,把线程归还给线程池(或调用方),直到这个Task完成后,方法才会从暂停的位置继续执行。而同步代码里的等待是让线程一直处于闲置状态,这段时间线程什么都做不了。
2. 示例代码的局限性
你贴的示例用Task.Run执行CPU密集型循环,这种场景下await的优势不明显。但如果把Method1换成IO密集型操作(比如数据库查询、HTTP请求、文件读写),差异立刻就出来:
- 同步代码:线程会一直阻塞,直到IO操作完成,这段时间线程完全闲置;
- 异步代码:await会释放线程,线程可以去处理其他请求或任务,IO完成后再回来继续执行后续代码。
3. 为什么示例输出和同步代码一致?
因为你的异步示例里,Method2是同步执行的,执行完才会调用Method1并await它。如果想看到并发效果,可以同时启动多个异步任务再await,比如:
static async Task Main(string[] args) { // 同时启动两个异步任务 var task1 = Method1(); var task2 = Method4(); // 另一个异步方法 Method2(); // 等待两个任务都完成 var count = await task1; var result = await task2; Method3(count); Console.ReadKey(); }
这时Method1和Method4的任务会在后台并行执行,同时Method2也在同步执行,就能看到异步并发的效果。
4. async/await的核心价值
async/await不是为了让CPU密集型代码“同时运行”(那是多线程的典型场景),它的核心是提升IO密集型场景下的线程利用率,避免线程被闲置浪费。比如在Web服务中,一个异步接口可以用更少的线程处理更多的请求,因为线程不会被IO操作阻塞。
内容的提问来源于stack exchange,提问作者Egor Bobrov

