C#面试题更新 2026-08-05

请从底层实现机制的角度,说明 C# 中 async/await 是如何工作的?

技术原理C#

考察说明

考查对 C# 异步编程模型底层原理的理解,包括状态机、同步上下文和任务调度。

回答思路

  1. 【回答框架 1】async/await 是编译器层面的语法糖,核心是生成一个状态机。当遇到 await 时,方法被拆分为多个状态,每次 await 都会保存当前执行位置和局部变量,并注册一个延续,即状态机的 MoveNext 方法。
  2. 【回答框架 2】状态机实现了 IAsyncStateMachine 接口,包含状态字段和构建器。编译器将方法体转换为一个结构体或类,通过 AsyncTaskMethodBuilder 来管理任务的创建和完成,以及异常的捕获和传播。
  3. 【回答框架 3】await 表达式会检查任务是否已完成,若已完成则直接继续执行;否则,会将状态机注册到任务的延续中,并返回一个不完整的任务给调用者,使当前线程可以继续执行其他工作。
  4. 【回答框架 4】任务完成时,延续会被调度执行。调度器默认使用当前同步上下文,若为 null 则使用线程池。这允许在 UI 线程上捕获上下文并在完毕后回到该线程,但并非总是如此,实际行为取决于上下文。
  5. 【回答框架 5】整个机制涉及异步方法的返回类型、可等待模式、线程上下文以及任务调度的协调,最终实现非阻塞的异步操作,但具体实现细节随版本可能有所变化。
  6. 【关键点 1】编译器将 async 方法转换为状态机,每个 await 点对应一个状态迁移。
  7. 【关键点 2】状态机保存局部变量并注册延续,避免阻塞线程。
  8. 【关键点 3】延续默认通过同步上下文或线程池调度,决定恢复线程。
  9. 【关键点 4】AsyncTaskMethodBuilder 管理任务状态和异常传播。
  10. 【易错点 1】异步方法不一定在另一个线程执行,await 本身不创建新线程。
  11. 【易错点 2】死锁可能因在同步上下文中阻塞异步方法而发生,如 UI 线程调用 Wait。
  12. 【易错点 3】延续调度的上下文不一定与起始相同,需明确同步上下文行为。