一句话总览
Swift 的
async函数在编译期会被转换成一个「显式状态机」:
- 函数体被拆成多个 suspend point(
await)之间的段- 局部变量被提升到 异步上下文(async context / frame)
- 控制流通过一个 state + switch 来恢复执行
await本质是:保存状态 → 挂起 → 未来某个时刻从状态机恢复
1️⃣ 从源码开始看
func foo() async -> Int {
let x = await bar()
let y = await baz(x)
return y + 1
}
编译器看到的关键信息:
-
foo是async -
有 2 个 suspension point
await bar()await baz(x)
👉 每一个 await 都是状态机的一个“切点”
2️⃣ 编译器的 lowering 阶段(AST → SIL)
Swift 的 async/await 主要在 SILGen(SIL Generation) 阶段被转换。
核心步骤是:
① 拆分函数为多个“continuation block”
逻辑上等价于:
state 0: 开始
state 1: await bar() 返回后
state 2: await baz(x) 返回后
② 局部变量提升(heap / context storage)
在同步函数中:
let x = ...
可以在栈上。
但在 await 之后还能用的变量:
let x = await bar()
let y = await baz(x)
⚠️ x 跨 suspension point 存活
➡️ 编译器会把它提升到 async context(也叫 frame) 中:
AsyncContext {
state: Int
x: Int
y: Int
}
3️⃣ 状态机的“伪代码形态”
编译器最终生成的结构,概念上接近这样:
func foo_async(context: inout AsyncContext) {
switch context.state {
case 0:
context.state = 1
await bar { result in
context.x = result
resume foo_async(context)
}
case 1:
context.state = 2
await baz(context.x) { result in
context.y = result
resume foo_async(context)
}
case 2:
return context.y + 1
}
}
🔑 关键点:
-
state决定从哪一段继续执行 -
每个
await:- 保存当前
state - 发起异步调用
- 注册 continuation
- 返回(函数“挂起”)
- 保存当前
-
恢复时再次进入同一个函数,但
state已经改变
4️⃣ Swift 不用“真正的 switch”,而是用 continuation
Swift 实际上比上面更精巧:
真实机制是:
-
async函数 = 隐式 continuation + resume function -
await会调用:swift_task_suspend- 保存 resume point
-
恢复时:
- runtime 调用 resume function
- 从正确的 SIL block 继续执行
你可以把它理解成:
状态机 + 函数指针 = continuation
5️⃣ 和 C++ / Kotlin / JS 的区别
Swift 的特点
| 语言 | 状态机位置 |
|---|---|
| Swift | 编译期(SIL) |
| Kotlin | 编译期(Continuation 参数) |
| C++20 coroutine | 编译期(promise / frame) |
| JavaScript | 编译期 + VM 支持 |
Swift 的 async:
- 零反射
- 无隐式线程切换
- 完全静态分析
- 性能接近手写状态机
6️⃣ 关键编译器组件(想看源码)
如果你要继续深挖源码,这些是入口:
SILGenFunction::emitAsyncFunctionAsyncLoweringSuspendContinuationswift_task_switchswift_task_suspend
路径大致在:
lib/SILGen/
lib/SIL/
lib/IRGen/
stdlib/public/Concurrency/
7️⃣ 一个容易忽略但很重要的点 ⚠️
async并不等于线程切换
-
await≠ yield thread -
状态机恢复在哪个 executor 上,由:
- actor
@MainActor- task priority
- runtime 决定
8️⃣ 总结一句话(拿去面试用)
Swift 的
async/await在编译期被 lowering 成一个显式状态机:
函数被拆成多个 suspension block,跨await的局部变量被提升到 async context,
每个await保存当前状态并注册 continuation,恢复时通过 resume 函数从正确的状态继续执行。