1218 字
3 分钟
Actor 模型(Actor Model)
一、为什么需要 Actor 模型
并发编程最头疼的问题是什么?共享可变状态。两个线程同时修改一个变量,你需要加锁;锁加多了,死锁就来了;锁粒度粗了,性能又上不去。更麻烦的是,bug 难以复现——今天跑得好好的,明天换个负载就崩了。
Actor 模型从根本上消除了这个问题。它的核心主张是:不要共享状态,只传递消息。每个 Actor 独占自己的状态,外部只能通过发消息来影响它,而 Actor 一次只处理一条消息,所以不需要锁。没有共享状态,就没有数据竞争;没有锁,就没有死锁——至少理论上如此。
Erlang 用这套模型在电信系统里跑了三十多年,实现了「九个九」(99.9999999%)的可用性。Akka 把它带到了 JVM 生态。游戏服务器、聊天系统、物联网平台——这些天然适合 Actor 模型的场景,都有一个共同特点:大量独立实体各自维护状态,偶尔交互。
二、现实类比
同事之间只通过密封信件和信箱沟通。没人直接走进别人的办公室翻他的文件——你写一封信,投到他的信箱,然后回去继续自己的工作。每个人一次只处理一封信,处理完再拆下一封。
三、核心思想
Actor 是拥有私有状态和信箱(消息队列)的轻量级进程。Actor 之间仅通过发送异步消息通信。每个 Actor 一次处理一条消息,更新状态并可选地向其他 Actor 发送消息。
flowchart LR
subgraph ActorA["Actor A"]
A1[State: count=3]
A2[Mailbox: m1 m2 m3]
A3[Processing: m1]
end
subgraph ActorB["Actor B"]
B1[State: items=[]]
B2[Mailbox: m4 m5]
B3[Processing: m4]
end
subgraph ActorC["Actor C"]
C1[State: total=0]
C2[Mailbox: m6]
C3[Idle]
end
ActorA -- send --> ActorB
| 属性 | 值 |
|---|---|
| 并发 | 无共享状态——仅消息传递 |
| 处理 | 每个 Actor 串行(一次一条消息) |
| 故障隔离 | Actor 崩溃不会损坏其他 Actor |
| 可扩展性 | 数百万轻量级 Actor(Erlang: 每进程约 2KB) |
四、变体与对比
| 模式 | 关系 | 区别 |
|---|---|---|
| 事件循环 | 每个 Actor 类似信箱上的单线程事件循环 | 事件循环侧重 I/O 多路复用;Actor 侧重状态隔离和消息传递 |
| 观察者模式 | Actor 通过消息通信类似发布/订阅 | 观察者是对象间通知机制;Actor 是并发计算模型 |
| CSP 模型 | 都通过消息通信 | CSP(Go channel)强调同步通信和通道;Actor 强调异步发送和信箱 |
| 状态机 | Actor 行为通常遵循状态机模式 | 状态机描述行为转换;Actor 描述并发实体 |
Warning
Actor 没有共享状态和锁,但不意味着不会死锁。如果 Actor A 等待 Actor B 的回复,同时 Actor B 也在等待 Actor A 的回复,双方都无法处理对方的消息——这本质上就是死锁。
五、多语言实现
Go:基于 goroutine + channel 的 Actor
Go 没有内置 Actor 框架,但 goroutine + channel 天然适合实现 Actor 模式:
type Actor struct { state interface{} mailbox chan interface{} handler func(state interface{}, msg interface{}) interface{}}
func NewActor(initial interface{}, handler func(interface{}, interface{}) interface{}) *Actor { a := &Actor{ state: initial, mailbox: make(chan interface{}, 100), handler: handler, } go a.run() // 启动 Actor 的消息循环 return a}
func (a *Actor) Send(msg interface{}) { a.mailbox <- msg}
func (a *Actor) run() { // 一次只处理一条消息,天然串行 for msg := range a.mailbox { a.state = a.handler(a.state, msg) }}TypeScript:带信箱的 Actor
type MessageHandler<S> = (state: S, msg: unknown) => S;
class Actor<S> { private state: S; private mailbox: unknown[] = []; private processing = false;
constructor(initialState: S, private handler: MessageHandler<S>) { this.state = initialState; }
send(msg: unknown): void { this.mailbox.push(msg); if (!this.processing) this.processMailbox(); }
private processMailbox(): void { this.processing = true; while (this.mailbox.length > 0) { const msg = this.mailbox.shift()!; this.state = this.handler(this.state, msg); } this.processing = false; }
getState(): S { return this.state; }}
// 使用示例:计数器 Actorconst counter = new Actor({ count: 0 }, (state, msg) => { if (msg === 'increment') return { count: state.count + 1 }; if (msg === 'decrement') return { count: state.count - 1 }; return state;});counter.send('increment');counter.send('increment');六、生产验证
- Erlang/OTP — erl_process.h#L1043-L1205:BEAM 虚拟机中进程的表示,
sig_qs是消息队列(信箱),每个进程拥有独立堆和信箱,轻量级 Actor 的工业级实现 - Akka (Scala) — Actor.scala#L476-L547:核心 Actor 接口,通过偏函数
receive指定消息处理行为,aroundReceive是分发钩子 - Actix (Rust) — Rust 生态中最流行的 Actor 框架,基于 Tokio 运行时,提供类型化消息和监督机制
七、小结
何时使用:
- 分布式系统——Actor 自然映射到网络节点(Erlang/OTP、Akka Cluster)
- 游戏服务器——每个实体(玩家、NPC、房间)作为独立 Actor
- 聊天系统——每个对话/聊天室作为一个 Actor
- 电信/物联网——大量并发会话,每个设备作为 Actor
何时不用:
- 紧密数据耦合——组件需要频繁共享可变状态时,消息传递增加延迟
- 简单请求-响应——函数调用比 Actor 往返更简单直接
- 计算密集无并发——没有并发收益的 Actor 只有开销
- 强一致性需求——Actor 提供最终一致性;ACID 事务需要其他机制
八、参考资料
- Erlang/OTP 官方文档 - Erlang 进程(Actor)参考手册
- Akka Actor 文档 - Akka Typed Actor 使用指南
- Hewitt 原始论文 - 1973 年,Carl Hewitt 提出的 Actor 模型原始论文
- Actix 框架文档 - Rust Actor 框架官方教程
- 「Let It Crash」哲学 - Erlang 容错设计哲学详解
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
Actor 模型(Actor Model)
https://blog.souloss.com/posts/programming/concurrency/concurrency-actor-model/ 部分信息可能已经过时
相关文章 智能推荐
1
Channel 通道 / CSP 模型(Channel / CSP)
程序设计 通过通信共享内存,而非共享内存通信——Go 并发模型的根基
2
Thread-Local Storage 线程本地存储(Thread-Local Storage)
程序设计 每个线程私有数据,消除竞争——连接池、随机数生成器的基础机制
3
MVCC 多版本并发控制(MVCC)
程序设计 通过保存数据的多个版本让读操作不阻塞写操作
4
信号量(Semaphore)
程序设计 用计数器控制同时访问共享资源的线程数
5
逻辑时钟(Logical Clock)
程序设计 在没有全局时钟的分布式系统中用逻辑计数器为事件排序






