Actor 模型思想总结

一句话总结:Actor 模型将计算系统理解为一个"社会"——无数独立行动者通过消息传递协作,没有共享内存,没有中央控制,这从根本上消除了并发编程中锁与竞态的复杂性。

流程图

单进程阻塞:一次服务一人

fork多进程:每人一个进程

pthread多线程:共享内存+锁

epoll事件驱动:回调地狱

Lua协程:同步写法异步执行

Actor模型+协程 = Skynet

规矩1:私有状态,不共享

规矩2:通信只能靠消息

规矩3:收到消息,自己决定

物理启发:相对论无全局状态

社会隐喻:一群人发消息协作

工程结果:无锁、故障隔离、天然分布式

内容梳理

一、历史演进:并发模型的六代更迭

我对 Actor 模型的理解,必须放在并发编程的历史演进中才能看清它的位置:

  • 第一代:单进程阻塞 — 一次只能服务一个连接,accept() 阻塞后什么也做不了
  • 第二代:fork 多进程 — 每个连接 fork 一个子进程,内存隔离但 IPC(Inter-Process Communication - 进程间通信)困难,fork 开销大,撑不住高并发
  • 第三代:pthread 多线程 — 用线程代替进程,共享内存、通信方便,但引入了锁和竞态条件,随着系统膨胀,锁的管理成为噩梦
  • 第四代:epoll 事件驱动 — 单线程非阻塞 I/O,一个事件循环处理所有连接,解决了锁的问题,但把业务逻辑撕成了碎片化的回调(回调地狱),没法用 if/else/while 写连续逻辑
  • 第五代:Lua 协程 — 利用 setjmp/longjmp 在用户态切换执行上下文,实现"同步写法 + 异步执行",解决了回调地狱问题
  • 第六代:Actor 模型 + 协程 = Skynet — 在协程之上加上 Actor 模型的隔离原则,每个 Actor 有独立的 Lua 虚拟机(lua_State),只通过消息通信,既没有锁问题,又能热更新、故障隔离

二、Actor 模型的三条规矩

Actor 模型的核心是三条简单规则,它们的共同目标是消除"共享":

  1. 每个 Actor 有且只有自己的私有状态 — 变量不共享,没有锁的必要。对应 Skynet 中每个 Service 有独立的 lua_State,全局变量互不可见
  2. 通信只能靠发消息 — 消息携带的是数据拷贝而非指针,接收方改拷贝不影响发送方。对应 skynet.send()(异步)和 skynet.call()(同步)
  3. 收到消息后只能做三件事 — 修改自己的私有状态、创建新的 Actor、给其他 Actor 发消息。不能读别人的状态,想知道就发消息问

三、为什么叫"Actor":Carl Hewitt 的哲学根基

“Act”(行动)这个命名不是随意取的,背后有三层隐喻:

语言学隐喻 — “说话本身就是行动”
Hewitt 受哲学家 John Austin 的言语行为理论(Speech Act Theory)启发。Austin 认为语言不只是描述世界,更是在改变世界——老板说"你被开除了",这句话本身就是一次改变世界状态的行为。Hewitt 将此引入计算模型:Actor 发出的每一条消息,不只是传递数据,而是一次行动,改变接收方的后续行为。

物理学隐喻 — “没有全局现在”
Hewitt 从广义相对论借了一个直觉:相对论中没有绝对的全局参考系,信息传递有速度上限。对应到计算系统:不应该假设全局时钟和全局状态。从量子力学又借了一个直觉:观察行为会干扰系统——你无法在不影响计算的情况下窥视 Actor 内部的调度。

社会学隐喻 — “计算是一个社会,不是一台机器”
这是最核心的隐喻。图灵机模型描述的是一颗孤立的大脑在思考,但真实世界的分布式系统——互联网服务、公司组织、生态系统——是无数独立个体在持续互动。每个个体有自己的知识(私有状态),通过语言交流(消息传递),自己决定如何回应(自主调度)。

四、Carl Hewitt 的核心思想与后期争议

Hewitt 思想的演变可以分为两个阶段:

早期(1970s-1980s,公认的奠基性贡献)

  • 1973 年发表 Actor 模型原始论文,导师为 Seymour Papert 和 Marvin Minsky("Society of Mind"提出者)
  • Actor 模型影响了 Scheme(Sussman 和 Steele 发现 Actor 创建操作和 lambda 是等价的)、Erlang、以及 Robin Milner 的 π 演算(Milner 的图灵奖演讲中明确感谢了 Hewitt)

后期(2000s 以后,争议越来越大)

  • 提出"Inconsistency Robustness"(不一致鲁棒性):大型分布式系统的信息天然矛盾,经典逻辑的反证法推理在真实系统中是一个"bug"
  • 声称 Actor 模型表达能力强于图灵机(存在 Actor 能表达的不可计算函数,非确定性图灵机表达不了),被理论计算机科学界广泛拒绝
  • 在 Wikipedia 上反复修改词条引发仲裁和封禁,被 arXiv 封禁

我的判断:Hewitt 早期的直觉是深刻且正确的(分布式系统需要无共享架构、消息传递是唯一正确抽象),但他的野心远超了自己的证明能力。工程实践中被验证的是 Actor 作为并发系统组织原则这一层,而不是那些形而上的声称。

五、从思想到工程:Skynet 中 Actor 的落地

在我正在学习的 openUBMC 项目中,Actor 模型的具体体现是:

  • 每个 .lua 文件不是一个"脚本",而是一个 Service(即一个 Actor)
  • skynet.start() 向 C 层运行时注册回调,C 层的事件循环(epoll + 消息队列)永不退出
  • skynet.send() 对应"Actor 间发消息"——异步通知,不等待回复
  • skynet.call() + skynet.ret() 构成同步请求-回复模式——调用方协程挂起,接收方处理完回复
  • skynet.dispatch() 注册消息处理器——“收到消息后自己决定怎么处理”
  • skynet.fork() 启动新的协程处理并发任务——同一个 Service 内可以有多个协程"并行"执行
  • 每个 Service 崩溃不影响其他 Service——天然的故障隔离

总结与展望

总结

  • Actor 模型用"不共享"替代了"用锁保护共享",从根本上消灭了死锁和竞态
  • "计算是社会而非机器"这一隐喻,是理解分布式系统最有力的思维框架
  • Skynet 将 Actor 实现为独立 lua_State + 消息队列,是工程上验证过的成熟方案
  • Hewitt 的物理/语言学灵感给了 Actor 模型哲学深度,但工程实践只需取其架构原则

展望/趋势

  • 现代大规模分布式系统(微服务、Kubernetes 编排、Serverless)本质上都在践行 Actor 模型的三条规矩,只是换了名字
  • Erlang/OTP 的 gen_server、Akka(Scala/Java)、Orleans(.NET)都是 Actor 模型的同类实现,对比学习有助于理解各实现的取舍
  • Actor 模型在边缘计算和 IoT 场景有天然优势——设备天然是物理隔离的,消息传递是唯一通信方式
  • 建议后续在 Skynet 实战中,刻意体会"消息驱动"和"共享内存驱动"两种思维的差异——这比记住 API 更重要
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐