请解释 C# 异步编程模型中 async 和 await 的底层实现机制,包括它们如何协同工作以实现非阻塞执行。
考察说明
考察对 C# 异步编程底层状态机、线程上下文和同步上下文的理解。
回答思路
- 【回答框架 1】async 和 await 是编译器特性,它们将异步方法编译成一个状态机。编译器将方法中的代码分割成多个部分,每个 await 表达式处生成一个状态。当方法执行到 await 时,若操作未完成,立即返回一个未完成的任务。
- 【回答框架 2】状态机实现了 IAsyncStateMachine 接口,包含状态字段和构建器。每次恢复时,状态机从 MoveNext 方法中根据当前状态继续执行。状态机保存了局部变量和 await 的上下文。
- 【回答框架 3】await 表达式默认捕获当前同步上下文(SynchronizationContext)或 TaskScheduler,在操作完成后通过上下文调度后续代码。若没有同步上下文,则使用线程池。这保证了 UI 线程上不会阻塞。
- 【回答框架 4】在线程池线程上,同步上下文为默认线程池调度器,因此异步操作通常在 IO 完成回调的线程上继续,避免了线程阻塞。CPU 密集型操作则可能在线程池上执行。
- 【关键点 1】async/await 是编译器状态机,不是新线程。
- 【关键点 2】await 会尝试捕获并恢复同步上下文。
- 【关键点 3】异步方法返回 Task 表示操作完成,不代表操作在线程上执行。
- 【关键点 4】状态机保存局部变量和恢复点。
- 【易错点 1】认为 async 方法会创建新线程,其实可能仍在调用线程上执行。
- 【易错点 2】忽略同步上下文捕获,可能导致死锁,例如在 UI 线程上阻塞等待异步任务。
- 【易错点 3】滥用 async void 可能导致未观察异常。