1️⃣ 核心结论
Actor 之间的消息传递是异步的(async),每个消息封装为任务排入目标 Actor 的 executor 队列,保证同一时间只有一个任务访问 Actor 内部状态,从而天然线程安全。
换句话说:
- Actor 内部属性永远不会被多个线程同时访问
- 跨 Actor 调用必须使用
await - executor 串行化任务执行
2️⃣ 消息传递的本质
① 什么是消息
actor BankAccount {
func deposit(_ amount: Int) {
balance += amount
}
}
- 当一个 Actor A 调用 B 的方法:
await account.deposit(100)
- 编译器和 runtime 会做三件事:
-
封装调用为任务(message)
- 包含方法、参数、continuation
-
放入目标 Actor 的 executor 队列
- 任务排队执行
-
调用者 await 任务完成
- await 等待任务执行完成返回结果
这就是异步消息传递,而非直接函数调用。
② executor 队列保证串行性
- 每个 Actor 的 executor 维护一个任务队列
- 同一时间只允许一个任务执行 Actor 内部代码
- 所以 Actor 内部属性访问 天然串行化
Actor Executor Queue
├─ Task 1
├─ Task 2
└─ Task 3
- 任务逐个执行 → 无竞争
3️⃣ 线程安全是如何保证的
✅ 通过隔离和串行执行
-
状态隔离
- Actor 内部属性只能通过 Actor 的 executor 访问
-
串行任务执行
- 每个任务完成前,队列中的下一个任务无法访问属性
-
异步消息
- 跨 Actor 调用必须 await,调用者等待执行完成,不能直接并发修改
结果:Actor 内部属性永远不会被多线程同时访问 → 无数据竞争
4️⃣ 示例
actor BankAccount {
private var balance: Int = 0
func deposit(_ amount: Int) {
balance += amount
}
func transfer(to other: BankAccount, amount: Int) async {
if balance >= amount {
balance -= amount
await other.deposit(amount)
}
}
}
transfer调用跨 Actorother.deposit- 编译器强制
await→ 消息异步发送给other的 executor - 目标 Actor 的状态不会被并发访问
- 线程安全由 executor 串行执行保证
5️⃣ Actor 消息传递 vs 同步调用
| 维度 | Actor 消息传递 | 同步函数调用 |
|---|---|---|
| 执行 | 异步排队 | 立即执行 |
| 线程安全 | ✅ 单线程串行访问 | ❌ 多线程需要锁 |
| await | 必须 | 不需要 |
| 可组合性 | 高(跨 Actor async/await) | 低(容易死锁) |
6️⃣ 直观比喻
- Actor = “单线程仓库 + 消息队列”
- 调用跨 Actor = “发请求给仓库,让仓库排队处理”
- await = “等待仓库处理完成返回结果”
- 内部状态不会被多个线程同时访问 → 安全
7️⃣ 面试必背总结
在 Swift 中,Actor 之间的消息传递是异步的,每个调用被封装为任务放入目标 Actor 的 executor 队列,串行执行 Actor 内部逻辑;这种机制保证了 Actor 内部状态天然线程安全,无需锁,也消除了数据竞争和死锁风险。