Chapter 6. Async implementation
覆盖generated code structure、simple MoveNext、control-flow lowering、execution context flow与custom task builders,建立async底层状态机模型。
学习目标
- 能描述async stub、generated state machine、awaiter fields与method builder的职责并绘制first-call/resume timeline
- 能推导if、loop、try/catch/finally跨await时的MoveNext states、hoisted locals与cleanup edges
- 能区分ExecutionContext、SynchronizationContext、TaskScheduler和custom builder,复现logical context flow
机制总览
Chapter 6. Async implementation:机制路径
- 1
为什么理解Async Lowering能消除错误直觉
Source看起来在await处“暂停方法”,runtime实际上只看到generated type与continuation calls。Compiler把需要跨suspension保存的locals提升成fields,用state选择resume point,用awaiter通知completi…
- 2
Structure of the generated co…
概念上,原method变成stub:创建state machine,初始化builder与initial state,调用builder.Start触发第一次MoveNext,然后返回builder.Task。Generated type在无suspension fast path可能保持struc…
- 3
A simple MoveNext() implement…
MoveNext以state选择入口:initial path执行同步前缀;遇await获取awaiter,若未完成则保存awaiter与next state,调用builder的Await(On/UnsafeOn)Completed注册continuation并return;resume后恢复aw…
章级决策实验
Chapter 6. Async implementation:机制与证据
切换《Chapter 6. Async implementation》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么理解Async Lowering能消除错误直觉
Source看起来在await处“暂停方法”,runtime实际上只看到generated type与continuation calls。Compiler把需要跨suspension保存的locals提升成fields,用state选择resume point,用awaiter通知completi…
可核验证据
以明确的 LangVersion 与目标框架构建「为什么理解Async Lowering能消除错误直觉」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。
学完《Chapter 6. Async implementation》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
Chapter 6. Async implementation:失效与核验
为什么理解Async Lowering能消除错误直觉
典型失效
若解释「为什么理解Async Lowering能消除错误直觉」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。
核验证据
以明确的 LangVersion 与目标框架构建「为什么理解Async Lowering能消除错误直觉」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。
Structure of the generated co…
典型失效
若解释「Structure of the generated co…」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。
核验证据
以明确的 LangVersion 与目标框架构建「Structure of the generated co…」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。
A simple MoveNext() implement…
典型失效
若解释「A simple MoveNext() implement…」时混淆语言规范、编译器降级、运行时和类库责任,版本变化后就会把实现细节误当作 C# 语义保证。
核验证据
以明确的 LangVersion 与目标框架构建「A simple MoveNext() implement…」的正反案例,并用编译诊断、生成 IL、运行轨迹或分配数据核对实际边界。
为什么理解Async Lowering能消除错误直觉
Source看起来在await处“暂停方法”,runtime实际上只看到generated type与continuation calls。Compiler把需要跨suspension保存的locals提升成fields,用state选择resume point,用awaiter通知completion,用builder完成Task。这个模型解释同步fast path、allocation、exception timing和context flow。
先预测:每个async调用是否必然new一个heap state machine;await前locals是否全部hoist;fault是否由awaiter.OnCompleted抛出;ExecutionContext是否等于SynchronizationContext。答案都是否。
↡compiler把async source改写为stub与保存program counter、locals、awaiter和builder的generated type。Structure of the generated code
概念上,原method变成stub:创建state machine,初始化builder与initial state,调用builder.Start触发第一次MoveNext,然后返回builder.Task。Generated type在无suspension fast path可能保持struct且避免heap allocation;一旦需异步continuation,runtime/builder可box或持有其state。具体优化随compiler/runtime变化,semantics不依赖layout。
只有跨await仍live的locals需要hoist,temporary若在suspension前已结束可保持stack/local。Debug build常生成更易调试但不同形状的code;不要把某次decompile字段数量当language guarantee。
// Conceptual lowering, not compiler-emitted source.
var machine = new StateMachine { state = -1, builder = Builder.Create() };
machine.builder.Start(ref machine);
return machine.builder.Task;切换stub、state、awaiter field与builder
A simple MoveNext() implementation
MoveNext以state选择入口:initial path执行同步前缀;遇await获取awaiter,若未完成则保存awaiter与next state,调用builder的Await(On/UnsafeOn)Completed注册continuation并return;resume后恢复awaiter、清field/state,调用GetResult并继续。
Method正常结束调用builder.SetResult;任何未处理exception由outer try/catch捕获并SetException。Cancellation通常也是exception path进入Task canceled semantics,取决于builder/associated token rules。Awaiter completion callback本身负责再次调用MoveNext,而不是重新调用原method。
if (!awaiter.IsCompleted)
{
state = 0;
savedAwaiter = awaiter;
builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
return;
}
var result = awaiter.GetResult();How control flow affects MoveNext()
If/loop只需把source control graph切分到suspension points;loop index、enumerator与accumulator若跨await则hoist。Serial loop每iteration await一次,语义正确但可能不满足throughput;是否parallelize是API decision,不是lowering优化。
Try/catch/finally更复杂,因为exception region与pending leave必须跨states保留。Awaited task的fault在GetResult处抛入MoveNext,因此source catch可处理;finally在normal、fault或early exit路径运行。Async iterator还有另一套builder/protocol,不能把普通async Task lowering原样套用。
切换if、loop、try/finally与catch
Execution contexts and flow
ExecutionContext承载AsyncLocal、culture/security等logical ambient state,通常随async flow捕获/恢复。SynchronizationContext是environment提供的continuation scheduling abstraction,如UI single-thread dispatcher;TaskScheduler调度Tasks。三者可以交互但不是同一对象或“当前线程”的同义词。
Awaiter的OnCompleted要求flow ExecutionContext,UnsafeOnCompleted允许builder/runtime优化flow handling,但application不应据方法名猜ambient state丢失。ConfigureAwait(false)主要请求不捕获current context用于continuation;它不保证thread pool、不抑制ExecutionContext,也不改变operation本身。
AsyncLocal方便trace/correlation,却会形成implicit dependency与retention。测试应在nested tasks、suppressed flow和parallel branches中验证,核心domain仍优先显式参数。
correlationId.Value = request.Id;
await SaveAsync(request, token).ConfigureAwait(false);
logger.LogInformation("Saved {CorrelationId}", correlationId.Value);Custom task types revisited
Task-like type通过AsyncMethodBuilderAttribute关联builder。Builder需Create、Start、Task、SetResult/SetException及await registration methods,并与awaiter protocol一致。它可以优化domain-specific completion,但若错误处理boxing、multiple await、context flow或exception,会破坏language期待。
Custom builder conformance至少覆盖:无await同步完成、一个/多个pending await、throw before/after await、cancellation、nested async、context/ExecutionContext、debugger与multiple consumers。没有这种test matrix和profile,不值得替换Task。
↡compiler通过AsyncMethodBuilder连接generated state machine与custom task-like result的可替换协议。切换ExecutionContext、SyncContext、scheduler与builder
本章回顾:Source Control Flow被精确编码而非消失
- Stub创建并启动state machine,builder把generated execution连接到caller task。
- Incomplete await保存next state/awaiter/live locals并注册continuation;complete fast path不suspend。
- MoveNext中的GetResult恢复source exception semantics,SetResult/SetException完成task。
- Branch/loop/try/finally被切割成states,cleanup与catch仍按logical source scope工作。
- ExecutionContext、SynchronizationContext、TaskScheduler与builder必须分层解释和测试。
练习
问题 1:如何判断一个local是否会成为state-machine field?
问题 2:try/finally跨await时,fault如何传播并cleanup?
问题 3:ConfigureAwait(false)没有做什么?
术语表
名词解释
本章出现的专业名词,用大白话再讲一遍。
- async lowering contract
- MoveNext transition
- logical execution context
- continuation scheduling context
- method builder protocol
原版目录概念补充核对
以下条目补齐官方目录中容易被示例主线掩盖的概念。它们不重复罗列目录,而是明确每项概念的机制、适用边界和验收证据。
A simple MoveNext() implementation:机制、边界与证据
Chapter 6. Async implementation中的A simple MoveNext() implementation必须分清语言规范、编译器实现、运行时行为与基础类库 API 四层责任。固定 C# 语言版本和目标框架,用正向/负向编译案例、必要的 IL 或运行轨迹以及版本对照验证结论。
How control flow affects MoveNext():机制、边界与证据
Chapter 6. Async implementation中的How control flow affects MoveNext()必须分清语言规范、编译器实现、运行时行为与基础类库 API 四层责任。固定 C# 语言版本和目标框架,用正向/负向编译案例、必要的 IL 或运行轨迹以及版本对照验证结论。
Execution contexts and flow:机制、边界与证据
Chapter 6. Async implementation中的Execution contexts and flow必须分清语言规范、编译器实现、运行时行为与基础类库 API 四层责任。固定 C# 语言版本和目标框架,用正向/负向编译案例、必要的 IL 或运行轨迹以及版本对照验证结论。
Custom task types revisited:机制、边界与证据
Chapter 6. Async implementation中的Custom task types revisited必须分清语言规范、编译器实现、运行时行为与基础类库 API 四层责任。固定 C# 语言版本和目标框架,用正向/负向编译案例、必要的 IL 或运行轨迹以及版本对照验证结论。