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. 1

    为什么理解Async Lowering能消除错误直觉

    Source看起来在await处“暂停方法”,runtime实际上只看到generated type与continuation calls。Compiler把需要跨suspension保存的locals提升成fields,用state选择resume point,用awaiter通知completi…

  2. 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. 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。答案都是否。

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;
分步1 / 3

切换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原样套用。

分步1 / 3

切换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。

分步1 / 3

切换ExecutionContext、SyncContext、scheduler与builder

本章回顾:Source Control Flow被精确编码而非消失

  1. Stub创建并启动state machine,builder把generated execution连接到caller task。
  2. Incomplete await保存next state/awaiter/live locals并注册continuation;complete fast path不suspend。
  3. MoveNext中的GetResult恢复source exception semantics,SetResult/SetException完成task。
  4. Branch/loop/try/finally被切割成states,cleanup与catch仍按logical source scope工作。
  5. 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 或运行轨迹以及版本对照验证结论。

资料与写作方式声明

本章以C# in Depth, Fourth Edition, Chapter 6: Async implementation权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…