题记与说明
撰写日期: 2026-11-01
说明: 本报告由同日两份章节报告综合而成——纵轴章(历时研究:从 1962 年至今的完整发展脉络)与横轴章(2026-11-01 时间截面上的横向对比)。两章均基于 2026-11-01 当天通过 WebSearch / WebFetch 实际检索并验证的公开来源撰写;本综合稿消除了两章之间的重复表述与口径差异,保留全部来源链接。检索时点为 2026-11-01,2025 年 10 月之后的变化未逐项核实。本稿为按独立审读意见修订后的最终稿(统一层数口径、补齐悬空引用与缺失来源、新增"状态归属"维度与背压/量化口径、统一章节编号)。
TL;DR
线程、进程、async/await、Actor、消息队列是同一问题的五次回答,2023 年前后从对立走向合流。五者不同层:线程与 async/await 管进程内 I/O 等待,进程管崩溃隔离,Actor 管有状态并发与跨机分布,消息队列管服务间解耦、缓冲与重放。选型不问"用哪个模型",而问三件事:要不要跨网络、崩了要不要连坐、状态归谁。2026 年:虚拟线程让 Java 回归同步写法;async/await 退守流式背压与无轻量线程语言;进程因 AI 推理回潮;Actor 借 serverless 与 AI Agent 再增长;队列分化为重日志流与轻任务队列。真实系统从不单选,而是分层组合。
纵轴:发展脉络
1. 一条主轴,五次回答
线程、进程、async/await、Actor、消息队列并非五种独立的发明,而是同一道问题——如何在有限的资源上组织大量并发、并在并发之下组织协作——在不同硬件与负载约束下给出的五次历史性回答。纵轴上看,它呈现清晰的钟摆运动:
- 共享内存阵营(进程→线程→async/await→虚拟线程)不断在"同步写法"与"异步调度"之间往返;
- 消息传递阵营(Actor、CSP、消息队列/事件流)从理论形式化走向语言运行时,再走向网络中间件。
两者在 2023 年前后出现明显合流:虚拟线程让 Java 回到同步写法,async/await 阵营则补上"结构化并发"的任务生命周期管理。
关键时间线速览
| 年份 | 事件 | 阵营 |
|---|---|---|
| 1962–1968 | Dijkstra 信号量与 THE 系统,确立"进程"抽象 | 进程 |
| 1973 | Hewitt 等提出 Actor 模型 | Actor |
| 1978 | Hoare 发表 CSP | 消息传递 |
| 1986 / 1998 | Erlang 诞生 / 开源并随 OTP 发布 | Actor |
| 1993 | IBM MQSeries 发布 | 消息队列 |
| 1995 | POSIX 1003.1c(pthreads)定稿;Java 1.0 内置线程 | 线程 |
| 1999 | Dan Kegel 提出 C10K 问题 | 转折点 |
| 2004 | nginx 发布(事件驱动架构) | 事件驱动 |
| 2007 | RabbitMQ(AMQP 0-9-1)诞生 | 消息队列 |
| 2009 | Go 开源(goroutine/channel);Node.js 问世 | CSP / 事件循环 |
| 2011 | LinkedIn 开源 Kafka | 事件流 |
| 2012 / 2015 | C# 5.0 / Python 3.5 引入 async/await | async |
| 2018 | Kotlin 1.3 协程转正 | async |
| 2021 | Swift 5.5:async/await + 语言级 Actor | 合流 |
| 2022 | Python 3.11 TaskGroup;Akka 转 BSL 许可 | 结构化并发 / 波折 |
| 2023 | JDK 21 虚拟线程转正(JEP 444);Apache Pekko 分叉 | 虚拟线程 / Actor |
| 2024–2025 | Python 3.13/3.14 免 GIL 构建转正;Kafka 4.0 去 ZooKeeper;JDK 25 结构化并发第五预览 | 再分化 |
2.1 阶段一:理论奠基期(1962–1985)——进程、Actor、CSP 三大思想源头
并发模型此后五十年的全部争论,都在这一时期写下了"世界观分野"。
进程抽象的确立。 1962–1963 年,Edsger Dijkstra 在开发 THE 多道程序系统的过程中发明信号量(P/V 操作);1968 年他在 CACM 发表《The Structure of the "THE" Multiprogramming System》,以分层设计把"进程"确立为一等操作系统抽象——第 0 层调度器为上层提供"每个进程都独占一台机器"的幻象。1969 年起 Bell Labs 的 UNIX 让进程成为第一个大规模实用的并发抽象(Semaphore - Wikipedia);THE multiprogramming system - Wikipedia;Dijkstra 1968 CACM 论文)。
Actor 模型:从 AI 形式化到并发理论。 1973 年,Carl Hewitt、Peter Bishop 与 Richard Steiger 在 IJCAI 1973 发表《A Universal Modular ACTOR Formalism for Artificial Intelligence》——值得注意的是,它最初并不是一篇系统论文,而是面向人工智能的模块化形式化,主张"一切皆 actor"(IJCAI 1973 原文 PDF)。此后 Irene Greif 给出操作语义,Gul Agha、Henry Baker、Will Clinger 等人发展了理论;1975 年 Scheme 的设计备忘录自称是"试图理解 Actor 模型"的产物,足见其影响力溢出了并发领域。
CSP:通信作为基本原语。 1978 年,C.A.R. Hoare 在 CACM 发表《Communicating Sequential Processes》,核心论点极为激进:"输入输出是程序设计的基本原语,顺序进程的并行组合是程序组织的基本模式"(1978 年原文 PDF)。CSP 在 1985 年发展为严格的过程代数(Wikipedia: CSP);1983 年 INMOS 为 transputer 芯片设计的 occam 语言是 CSP 子集的直接实现;而在 Bell Labs 内部,Newsqueak、Alef、Limbo 一脉相传地把"进程 + 通道 + select"工程化——这条血统最终通向 Go(Russ Cox 的历史梳理 Bell Labs and CSP Threads)。
本阶段定下了此后五十年争论的两大坐标:共享内存 + 锁(进程/线程路线)与消息传递 + 无共享(Actor/CSP/队列路线)。驱动因素是分时系统与多道程序设计带来的互斥与死锁问题、理论计算机科学追求形式化的潮流,以及电信行业对"永不宕机系统"的早期想象。
2.2 阶段二:线程主流化期(1986–1999)——"每请求一线程"的黄金年代
Erlang:Actor 模型的第一次工业化。 1986 年,Ericsson 计算机科学实验室的 Joe Armstrong、Robert Virding 与 Mike Williams 开始设计 Erlang,目标是为电信系统提供极致可靠性(需求来自 99.9999% 可用性):轻量进程、进程间邮箱通信、监督树与"任其崩溃"哲学,是 Actor 模型迄今最彻底的工程实现。1998 年 Ericsson 开源 Erlang 并发布 Open Telecom Platform(OTP);耐人寻味的是同年 Ericsson 一度禁止新内部产品使用 Erlang,Armstrong 因此离职创办 Bluetail,2003 年以博士论文《Making reliable distributed systems》为 Erlang/OTP 正名(Erlang 历史梳理;Armstrong 2003 博士论文)。
1995:线程的双里程碑。 这一年,IEEE 1003.1c-1995 定稿 POSIX Threads(pthreads)标准——它是对此前 Solaris、Mach 等各厂商线程实现混战的统一,是行为规范而非具体实现(LLNL pthreads 教程);同年 Java 1.0 发布,把线程内建进语言本身,早期采用用户态 green threads,之后改为映射到原生线程。至此,"同步写法 + 阻塞 I/O + 每连接一线程"成为服务器编程的默认范式。2004 年 Java 5 的 java.util.concurrent(JSR-166,Doug Lea 主导)进一步把并发工具库标准化。
消息队列的企业线同步展开。 1993 年 12 月 IBM 发布 MQSeries(2002 年改名 WebSphere MQ,2014 年改名 IBM MQ),确立了消息队列作为企业级中间件的品牌(IBM MQ - Wikipedia);1998 年 JMS 为 Java 世界定义了消息中间件 API 标准;微软 MSMQ 在 1990 年代末进入 Windows。
这一时期线程模型的适用边界尚未被质疑:SMP 多处理器服务器普及使"用线程吃满多核"成为刚需,UNIX 厂商 API 大混战倒逼 POSIX 统一,电信与金融业分别从可靠性与异步解耦两端推动了 Actor 语言和消息队列——而服务器并发量级还在"每连接一线程"的舒适区内。
2.3 阶段三:C10K 危机与事件驱动转向(1999–2008)——线程模型的第一次边界暴露
1999 年,Dan Kegel 发表《The C10K Problem》,问出一个时代之问:如何让一台服务器同时处理一万条并发连接?他系统列举了 thread/process-per-connection 模型在内存占用、上下文切换与文件描述符限制上的崩溃点,并推动操作系统完善 select/poll 之外的多路复用原语(kegel.com/c10k.html;Wikipedia: C10k problem)。Matt Welsh 2000 年前后的 SEDA 工作则从学术侧系统比较了工作线程与事件驱动两条路线的取舍。
事件驱动阵营给出工程答卷。 Igor Sysoev 开发的 nginx 于 2004 年公开发布:固定数量的 worker 进程,每个 worker 用事件循环(epoll/kqueue)驱动数万条连接,彻底放弃每连接一线程,成为 C10K 的标准答案。2009 年 Ryan Dahl 在 JSConf.eu 发布 Node.js,把 V8 引擎与 libuv 事件循环(统一抽象 epoll/kqueue/IOCP)绑定为默认运行时,"单线程事件循环 + 非阻塞回调"从此从系统程序员的专利变成前端工程师的日常;Dahl 本人在 2011 年的访谈中明确表示,动机正是规避 Apache 式每连接一线程的开销(Node.js 首次发布幻灯片;Dahl 访谈)。
消息队列的开源化。 Apache ActiveMQ(2004)成为 JMS 生态的开源旗舰;2007 年,LShift 与 CohesiveFT 的合资项目推出 RabbitMQ——基于公开的 AMQP 协议(RabbitMQ 采用 0-9-1 变体)的开源消息代理,并且用 Erlang 写成:Actor 语言的第一次大规模出圈(RabbitMQ AMQP 概念文档;AMQP - Wikipedia)。
线程模型的适用边界第一次被清晰划出:在 I/O 密集、海量连接场景下,"每任务一线程"不经济——但代价是回调风格的可读性灾难,这为下一阶段埋下伏笔。
2.4 阶段四:多核危机与语言并发复兴(2009–2016)——async/await 与 CSP 的全面回归
2005 年前后主频提升停滞,多核成为常态,Herb Sutter《The Free Lunch Is Over》宣告了"免费午餐"的终结(原文)。语言设计界以此为起点迎来并发复兴。
CSP 路线的工程化巅峰。 2009 年 11 月 10 日,Google 开源 Go 语言(Robert Griesemer、Rob Pike、Ken Thompson 设计):goroutine 是 KB 级、可百万并发的轻量执行体,channel 与 select 直接继承自 CSP 经 Bell Labs 一脉的谱系(Go 发布公告;Go FAQ;goroutine 初始栈 2KB 的官方变更见 go1.4 release notes)。Go 证明了一件事:可以用同步写法获得异步的规模——这正是对 C10K 时代"要么线程要么回调"二选一的否定。2012 年 3 月 Go 1.0 冻结 API。
async/await 语法的诞生与扩散。 这条线的谱系是:2007 年 F# 的 async workflows(Don Syme)最早实现 await 风格的计算表达式 → 2010 年 .NET 4 的 Task Parallel Library 提供 Task 基础设施 → 2012 年 C# 5.0 引入 async/await 关键字,成为第一个内置该语法的主流语言(Async/await - Wikipedia;A brief history of async/await)。随后扩散:2012 年 12 月 Guido van Rossum 提交 PEP 3156(代号 Tulip),2014 年 Python 3.4 落地 asyncio(生成器协程 + yield from);2015 年 Yury Selivanov 的 PEP 492 为 Python 3.5 带来原生 async/await——期间还有 PEP 492 与 PEP 3152 的方案之争(PEP 3156;PEP 492;LWN: PEP 492 vs 3152)。JavaScript 侧则由 ES2017(2017 年)标准化 async/await,收编了回调与 Promise 链。
Rust 与 JVM 的两条支线。 2015 年 Rust 1.0 发布,所有权系统把"无数据竞争"变成编译期承诺,"fearless concurrency"成为口号;Tokio 运行时(2016 年起)成为 Rust 异步生态的事实标准。JVM 侧,2013 年 Reactive Manifesto 发布,2015 年 Reactive Streams 规范 1.0 定稿并在 Java 9(2017)以 java.util.concurrent.Flow 吸收;RxJava、Project Reactor 等响应式流库一度被视为 JVM 高并发服务的主流答案(Reactive Streams)。
消息队列的范式革命。 2011 年 LinkedIn 开源 Kafka:持久化分区日志 + 消费组,把消息中间件从"投递队列"升级为"事件流平台",RabbitMQ 与 Kafka 由此分野为"消息代理"与"日志流"两种范式(ByteByteGo: 消息队列架构演进);2016 年 Yahoo 开源 Pulsar,引入存算分离与分层存储。Erlang/Actor 阵营则以 WhatsApp 为标志性战果——小团队用 Erlang 支撑数亿用户(参见 Erlang 历史梳理)。
这一阶段的驱动因素:多核硬件与 Web 规模化的双重倒逼;函数式思想的回流(monad、promise、continuation);以及各语言社区对"回调地狱"的集体反弹——async/await 本质上是把事件循环时代的手工状态机交还给编译器。
2.5 阶段五:结构化并发与虚拟线程的合流(2017–2023)——两大阵营互相靠拢
这一阶段的主旋律是互相学艺:async 阵营补生命周期管理,线程阵营补调度密度。
async 阵营:从语法到结构化。 2018 年 10 月 Kotlin 1.3 将协程转正(kotlinx.coroutines 同步发布 1.0),挂起函数让 JVM 开发者以同步写法跑在异步调度上(Kotlin 1.3 发布公告)。同年前后,Nathaniel Smith(Trio 作者)发表《Notes on structured concurrency, or: go statement considered harmful》,把"任务的作用域必须嵌套于其父任务"确立为设计原则(原文)。这股思想迅速制度化:2021 年 9 月 20 日 Swift 5.5 一次性内置 async/await、Actor、TaskGroup 与 AsyncSequence(Swift 5.5 发布公告)——语言级 Actor 模型时隔数十年回归主流语言;2022 年 10 月 Python 3.11 落地 asyncio.TaskGroup 与 ExceptionGroup(PEP 654),TaskGroup 拥有其作用域内创建的任务并在失败时自动取消、聚合异常。
Actor 阵营的框架波折。 2022 年 9 月,Lightbend 宣布 Akka(JVM 生态事实上的 Actor 标准)自 2.7 起从 Apache 2.0 改为 BSL 1.1 许可——年收入低于 2500 万美元的公司豁免,每个版本三年后回归 Apache 2.0(Lightbend 官方公告)。社区随即提案分叉,2023 年 Apache Pekko(基于 Akka 2.6.x 代码的 Apache 2.0 分叉)成为 ASF 顶级项目,Play Framework 3 与 Flink(FLINK-32468)等生态项目跟进迁移(Lunatech: 一年后回顾;Akka BSL FAQ)。这一事件再次印证了 Actor 模型的生存形态高度依赖"框架治理"而非"语言特性"。
线程阵营的大回摆:虚拟线程。 2023 年 9 月发布的 JDK 21 将虚拟线程转正(JEP 444,此前在 JDK 19/20 预览):由 JVM 而非操作系统管理的轻量线程,M:N 调度到少量 OS 线程上,使"thread-per-request"同步写法直接达到此前必须 async 才能企及的吞吐量(JEP 444)。同一版本中,结构化并发以 JEP 453 进入预览。虚拟线程的潜台词极其重要:Java 判定"函数染色"问题(async 关键字传染整个调用链)的代价高于运行时自动挂起的成本,于是选择用十年前 Project Loom 的思路终结 JVM 生态在 CompletableFuture/RxJava/Reactor 之间的碎片化。同期 Rust 1.75(2023 年 12 月)稳定了 trait 中的 async fn(官方博客),但明确不含动态分发。
这一阶段的驱动因素:十年 async 实践暴露的"染色函数"税、取消与超时难以正确传播、栈追踪支离破碎;分布式系统对任务生命周期可推理性的要求提高;以及开源商业化(Akka 事件)对治理模型的冲击。
2.6 阶段六:收敛与再分化(2024–2026,检索时点 2026-11-01)
线程模型的"多核合法性"恢复(Python)。 2024 年 10 月发布的 Python 3.13 带上实验性免 GIL 构建(PEP 703 第一阶段);PEP 779 定义的"受支持"门槛于 2025 年 6 月获指导委员会接受,free-threaded 构建随 Python 3.14(2025 年 10 月发布)进入 Phase II——官方支持、不再是实验品(GIL 构建仍是默认),CPython 的线程自此重新具备吃满多核的资格(Python 官方 free-threading 文档;PEP 703;PEP 779;py-free-threading;Python 3.13 性能评测)。
事件流平台的治理收敛。 2025 年 3 月,Apache Kafka 4.0 发布,彻底移除 ZooKeeper,KRaft(Raft 共识的元数据仲裁)成为默认与唯一运行模式,为沿用约十四年的外部协调服务画上句号(Confluent: ZooKeeper and Kafka Explained;Kafka 官网)。
JVM 结构化并发仍在打磨。 Java 25 LTS(2025-09-16 GA)以 JEP 505 带来结构化并发第五次预览:StructuredTaskScope 与 join 策略解耦,引入可配置的 joiner;JDK 26 以 JEP 525 进入第六次预览——社区对 API 人体工学仍有争议,转正时间未定;同版 JEP 506 Scoped Values 定稿(Project Loom;JEP 505)。
各阵营的 2025 动态。 Rust 侧,Tokio 成为事实标准运行时,async-std 于 2025-03-01 停更,生态整合到 Tokio/Smol,争议从"碎片化"转向人体工学(corrode.dev;JetBrains Rust 博客)。BEAM 侧,OTP 28(2025-05-20)加入优先级消息,把 BEAM 内的调度精细度又抬一档(OTP 28 highlights);Elixir 于 2025 年 8 月发布跨运行时互操作与可移植性博客,向外借生态的信号明显(elixir-lang.org)。Java 虚拟线程已进入 Netflix/Uber/Stripe/Amazon 等生产案例(Medium 2025 汇总)。
截至检索时点的格局判断。 五条模型线并未互相取代,而是完成了一次"各归其位":
- 进程:隔离与多语言部署的单元(容器即进程边界的工业化);
- 线程/虚拟线程:CPU 密集与默认并发的通用答案,虚拟线程接管了 I/O 密集场景的"同步写法"需求;
- async/await:I/O 密集、单机高扇出场景,且主要退守"运行时未内建轻量线程"的语言与流式背压场景,并已被"结构化并发"补全。此处"无轻量线程"按一个明确标准判定:运行时未内建"同步写法 + 运行时自动挂起"的并发原语——JS 的运行时只有事件循环(并发原语是回调/Promise);Rust 有 Tokio 异步任务,但必须 async 显式着色、非同步写法;Python<3.14 有 threading 却受 GIL 限制无法并行执行字节码,其 asyncio 同样要求着色——三者均落入此类;
- Actor:有状态、需容错与位置透明的分布式集群(Erlang/Elixir、Akka/Pekko、Orleans/Dapr,以及 Swift 的语言级 actor);
- 消息队列/事件流:跨服务、跨组织的异步解耦、削峰与事件溯源(Kafka 4.0、Pulsar、RabbitMQ)。
横轴:横向对比
3. 五个候选根本不在同一层
横向对比最大的陷阱是默认五者"同类可比"。实际上它们处于五个不同抽象层:
| 候选 | 实质 | 调度者 | 天然作用域 |
|---|---|---|---|
| 线程(OS 线程 / 虚拟线程 / goroutine) | 执行流抽象 | 内核(OS 线程)或语言运行时(虚拟线程、goroutine) | 单进程内(可借库扩展到机群) |
| 进程(multiprocessing、worker 池) | 资源隔离容器 | 内核 | 单机(可借 IPC/调度器扩展到机群) |
| async/await(协程、结构化并发) | I/O 等待期的控制权让渡 | 语言运行时/事件循环 | 单进程内 |
| Actor(Erlang/Elixir、Pekko/Orleans/Dapr) | 状态 + 信箱的封装单元 | BEAM 调度器 / 框架 | 单机起步,主流框架原生跨机 |
| 消息队列(Kafka、RabbitMQ、NATS 等) | 跨进程/跨服务的消息中转与缓冲 | 中间件自身 | 天然跨网络、跨服务 |
一句话说清边界雏形:线程/async/await 解决"一个进程内如何等待更多 I/O",进程解决"出事时如何不连坐",Actor 解决"有状态的东西如何被安全地并发访问并在机器间分布",消息队列解决"服务与服务之间如何解耦、缓冲和重放"。 它们不是互斥选项,而是分层可组合的积木。
4. 核心对比表
| 维度 | 线程(OS/虚拟/goroutine) | 进程 | async/await | Actor | 消息队列 |
|---|---|---|---|---|---|
| 并发单元成本 | OS 线程默认栈预留 1–8 MB;虚拟线程栈存堆上、KB 级;goroutine 初始 2KB | 最重:整套地址空间、内核对象,单机几十~几百个为常态 | 最低:任务多为状态机+小堆对象,单机可达百万级任务 | 轻:BEAM 进程 KB 级、单节点百万进程 | 不以"并发单元"计价,以消息吞吐与存储计费 |
| 通信方式 | 共享内存 + 锁(Go 部分遵循 CSP:channel) | IPC、管道、共享内存、socket | 同一内存空间,await 处让渡;需防跨 await 的数据竞争 | 信箱消息传递,无共享("share memory by communicating") | 经纪人中转:点对点/发布订阅/日志流 |
| 状态归属 | 无内建归属:共享堆内存,谁持锁谁负责(Rust 借所有权在编译期强制独占) | 独占:状态天然归进程所有,跨进程必须显式序列化 | 无归属约束:同一地址空间直接引用,任务间共享一切 | 最明确:状态归 Actor 独占,外界只能发消息 | 队列不持有业务状态,只持有在途消息;业务状态留在消费者侧 |
| 故障隔离 | 弱:同进程一损俱损;进程级强 | 最强:崩溃不传染 | 弱:一个 panic 可打断整个事件循环(取决于运行时) | 强:supervisor "let it crash" 是内建哲学 | 最强之一:消费者崩溃消息不丢(可重投) |
| 跨机/分布式 | 否(单进程;需自建 RPC/队列) | 否(单机;需外部调度) | 否(单进程) | 多数框架原生支持(Orleans/Dapr/Erlang distribution/Akka Cluster) | 原生分布式,这是其存在理由 |
| 背压 | 无内建(自行控制并发数) | 无内建 | 有(Reactor/RxJS 流式背压成熟;普通 asyncio 需手搓信号量) | 无内建全局背压(靠信箱上限/监控) | 强:队列长度、消费滞后(lag)天然可视可控 |
| 调试与可观测 | OS 线程有成熟工具;虚拟线程"池化"是反模式 | 成熟(进程级监控、OOM 隔离清晰) | 异步栈追踪困难是公认痛点(各语言持续改进) | 信箱积压/进程树监控内建(BEAM 观测工具链强) | 滞后、重投、幂等是日常运维主题 |
| 学习曲线 | 低→中(锁的正确使用难) | 低 | 中:函数着色与传染性 | 中:思维转换大,一旦掌握收益高 | 低起步、高天花板:至少一次/幂等/顺序性要吃透 |
5. 逐维度横向说明
成本与规模上限:数量级差距决定了"能不能堆人头"。 OS 线程默认栈预留 1–8 MB(Windows 默认 1 MB;Linux pthreads 常见 8 MB 虚拟预留——后者属公开资料常见口径,未逐条复核原始 URL,pthread_create(3)),加上内核调度与上下文切换成本,单机数千个即接近实用上限。goroutine 初始栈 2KB 且可增长收缩(Go 1.4 官方变更,go1.4 release notes),按 2–4 KB 估算,1 GB 内存可容纳数十万个 goroutine。Java 虚拟线程(JEP 444)栈存于堆上、仅在运行时挂载到载体线程,官方口径是"廉价而大量、永远不要池化"。Erlang/OTP 进程与之同量级,单节点可达百万进程。消息队列的计价单位完全不同——不是"每并发单元多少内存",而是每秒消息数与存储。2025 年基准(Onidel VPS 基准)给出方向性结论:NATS 内存 Pub/Sub P99 在亚毫秒~2ms 级,RabbitMQ 约 5–15ms,Kafka 持久日志约 15–25ms;但 Kafka 在持久化高吞吐与可重放日志上领先。基准数字高度依赖消息大小/持久化设置/ack 策略,只可作方向参考。 横向含义:当你需要"百万级并发等待",OS 线程与进程直接出局;当你需要"跨服务解耦",前四者都不提供答案——成本维度上五者是互补关系而非替代关系。
通信与共享:这一维度才是真正的分水岭。 五者最本质的差异不是性能,而是你被允许怎么共享数据(对应对比表"通信方式"与"状态归属"两行):
- 线程/async/await:共享内存是默认,正确性靠锁/原子操作/所有权规则(Rust 编译期强制)。async/await 的特殊性在于函数被"着色"后只能在 async 上下文中互调——这就是著名的 What Color is Your Function?(Nystrom, 2015)提出的批评;该争论在 2024-10 的 HN 讨论中仍活跃,Kotlin 协程负责人 Elizarov 的批评与 Rust 方"显式着色不是坏事"的辩护构成两极。
- Actor:默认不共享——每个 Actor 独占自己的状态,外界只能发消息。代价是所有交互都要建模为消息协议;收益是数据竞争从"运行时 bug"变成"不可能发生"。
- 消息队列:连"同一台机器"都不是前提,通信即网络。因此消息语义(至少一次/至多一次/恰好一次、顺序性、幂等消费)成为一等公民工程问题。
- 进程:隔离是目的,通信是代价(IPC 序列化),天然把"共享"降到最低。
故障隔离与容错:从"会不会连坐"到"崩了之后怎么办"。 进程是唯一在操作系统层面保证崩溃不传染的模型——这也是 AI 推理服务(一个请求可能 OOM 一块 GPU)重新倚重多进程的原因,且这一取向有据可查:主流推理基础设施正是按进程边界设计的——Ray Serve 把每个 replica 作为独立进程(Ray actor)运行,Controller 检测到 replica 崩溃即重建,托管机器宕机时 actor 也会在别的机器上自动重启(Ray Serve 架构文档);vLLM 自 V0.6 起把 API 前端与引擎拆成多个进程(ZeroMQ IPC),V1 更是把"隔离的 EngineCore"沉进核心架构,让模型侧故障不连带前端网关(vLLM V1 发布博客)。Actor 把隔离做进语言/框架层:supervisor 树、let it crash,Erlang/Elixir 凭此支撑 WhatsApp、Discord 级别的系统。消息队列的容错单位是"消息"而非"计算":消费者崩溃,消息可重投,但必须幂等——这是选队列就要付的税。线程与 async/await 共处同一地址空间,任何未捕获异常都可能终结整个进程/事件循环;Java 用结构化并发(JEP 505/525,截至检索仍未定稿)尝试把"任务组"作为取消与错误的边界(Project Loom)。
分布式能力:只有两类选手原生过得了网络。 Actor(Erlang distribution、Orleans/Dapr 虚拟 Actor、Akka/Pekko Cluster)与消息队列是仅有的两个"出生时就懂网络"的选项。Orleans 的虚拟 Actor 模型(MSR 2014 论文提出"按需激活、空闲回收")如今有两个主流载体:.NET 专属的 Orleans,与语言无关的 sidecar 运行时 Dapr(Actor 只是 pub/sub、状态、工作流等积木之一);2025 年末的社区文章开始把 Dapr Actor 用于 AI Agent 编排场景。Cloudflare Durable Objects 是 Actor 思想的 serverless 化:每个 key 一个全局唯一、单线程执行、可序列化一致性的对象;2024-09 起每个 DO 内嵌零延迟 SQLite 存储,D1 数据库亦构建于 DO 之上(Cloudflare 博客)。
背压:生产者过快时,谁能让上游"慢下来"。 这一维度是 TL;DR"async 退守流式背压"与辩论一"虚拟线程 vs 响应式"立论的关键,值得单独展开。消息队列是五者中唯一把背压做成一等公民的:消费者按自己的节奏拉取,队列深度与消费滞后(lag)天然可视可控(Kafka 消费组的位移滞后是监控面板上的常驻曲线,RabbitMQ 的 prefetch 计数是同一思想的另一形态)。async/await 内部分两档:响应式流(Reactor/RxJS)按 Reactive Streams 规范内建需求信号(demand signaling),背压成熟(Reactive Streams);而普通 asyncio/JS 事件循环默认没有背压,过载时任务在回调/微任务队列里堆积、内存膨胀,需要手搓信号量或有界队列。Actor 没有跨组件的全局背压——消息一旦进入信箱,生产者即不被反压(BEAM 的 send 永不阻塞);工程上的近似手段是有界信箱(Akka 可配置容量与溢出策略)与信箱深度监控(BEAM 的常用手段)。线程同样无内建,教科书做法是生产者/消费者之间放有界阻塞队列,靠"队列满则阻塞写入"实现反压。进程在框架层面也无内建;唯一"天然"的反压来自 OS 管道的固定缓冲——写满即阻塞(属操作系统教科书行为)——但常用的高层封装(如 Python 的 multiprocessing.Queue)默认无界,这一性质就丢失了。横向含义:需要"上游过载时自动减速"的流式链路,选队列或响应式流;其余模型都要自己补反压设计。
调试与可观测、学习曲线: 这两个维度已在第 4 节核心对比表中逐项给出结论,此处不再展开。要点各一句:调试侧,异步栈追踪困难与虚拟线程"勿池化"是最常踩的两个坑(表中"调试与可观测"行);学习侧,async 的函数着色与队列的消息语义(至少一次/幂等/顺序性)是最陡的两段坡(表中"学习曲线"行)。
6. 三个正在进行的"替代叙事"辩论
- "虚拟线程终结响应式编程?" 10 万请求压测类文章多判虚拟线程胜出(更简单、更易调试、吞吐相当或更好),但 Spring 官方 Office Hours 的回答是"不会"——Reactor/WebFlux 在背压、流式数据、细粒度并发控制上仍有正当场景(Stack Overflow 技术讨论;Reddit r/java 讨论)。2025–2026 的务实共识是混合:普通请求走虚拟线程,流式/背压场景保留 Reactor。
- "Postgres 取代消息队列(任务队列子品类)"。
FOR UPDATE SKIP LOCKED+LISTEN/NOTIFY让 Postgres 能胜任中等规模任务队列,省掉一套基础设施,并与业务事务同库一致。HN 上的核心反驳:SKIP LOCKED 队列在 worker 崩溃时会丢在途消息(worker 中途死亡时任务行会永远卡在running,需自建租约/janitor 式恢复逻辑),除非自建恢复逻辑;且它是"单消费者每消息"语义,天然不支持扇出/发布订阅(每个被认领的任务对其他消费者即告消失,多消费者各取一份要改用 Kafka 类日志);超高吞吐或需要重放时退化明显(Prisma: You Don't Need a Job Queue 对这三类局限有正面自认;社区两极争论另见 HN "On SQS" 讨论串,2019-05)。量级与迁移信号(经验口径,非基准结论): 综合检索到的实践文章,Postgres 任务队列的舒适区大致是持续每秒数百到数千条任务、单条消息 KB 级(大载荷建议放对象存储、队列只存引用)、保留期以天计;Prisma 文章自认的临界信号是"持续的每秒数千条会让 jobs 表的 vacuum 压力成为独立的运维问题,而专用中间件没有这个失败模式",并给出迁移原则——当你能说出自己撞上的是哪个限制(vacuum 压力、扇出、功能清单)时再迁移,且不要提前。归纳为四条"该换 Kafka 类"的信号:①吞吐持续高于每秒数千条;②需要多个消费者组各取一份完整事件流(扇出/发布订阅);③需要长保留期与重放;④消息体积达 MB 级。结论方向:任务队列(job queue)场景 Postgres 是真实竞品;事件流/日志流场景 Kafka 类仍无可替代。 - "Actor 即服务 / 函数即并发单元"。 Durable Objects、Dapr Actor 把 Actor 从"引入一个重框架"降为"调一个 API",边际成本大幅下降,2025 年起被广泛用于协作编辑、计数器、AI Agent 状态等有状态协调场景(Cloudflare DO 文档)。这压缩了"自建 Akka/Pekko 集群"的地盘,但把锁定风险换给了云厂商。
交叉洞察
单看纵轴得到的是"每种模型从哪来",单看横轴得到的是"每种模型现在站在哪"。两条轴交叉之后,才能回答本课题真正的问题:适用边界由什么决定、边界正在向哪里移动、选型者该问什么问题。
洞察一:每种模型的适用边界,都是其诞生时代约束的化石
纵轴上每个模型的诞生年份都精确对应一种当时的硬件/负载约束:线程对应 SMP 服务器的"吃满多核"刚需;事件循环对应 C10K 互联网的长连接洪峰;async/await 对应多核危机叠加回调地狱;Actor 对应电信 99.9999% 可用性;消息队列对应企业集成,再升级为流式时代的日志重放。横轴进一步显示:这些"化石"至今仍是选型的第一性依据——不是因为历史神圣,而是因为底层物理约束(栈内存、上下文切换成本、网络分区、序列化开销)六十年来只变了量级、没变性质。判断一个模型适不适合你的场景,等价于判断你的场景与哪个时代的约束最像。
洞察二:钟摆与合流——争论焦点已从"哪种模型正确"转为"运行时能隐藏多少差异"
纵轴显示 60 年历史呈钟摆运动:同步写法(线程)→ 异步调度(事件循环/async)→ 回到同步写法(虚拟线程、goroutine 本就是同步风格)。横轴的 2026 年截面恰好停在钟摆回摆点:Java 用虚拟线程承认"函数染色的代价高于运行时自动挂起",async/await 阵营则吸收结构化并发补全生命周期管理。两轴合读的结论是:主流运行时正在收敛于"同步写法 + 运行时自动挂起 + 结构化生命周期"的最大公约数——这正是 Project Loom(虚拟线程 + 结构化并发)与 Python free-threading 的共同方向。未来一门语言若没有内建轻量线程与结构化并发,将在默认并发体验上落后一个身位。
洞察三:五者是同心圆上不同半径的刻度,组合律优先于选型律
横轴证明五者不在同一抽象层(执行流 / 隔离容器 / 控制权让渡 / 状态封装 / 网络中转),纵轴证明它们从未真正互相取代(每一代"新范式"都只是在自己那一层继续演化:消息队列从 MQSeries 演化到 Kafka,Actor 从 Erlang 演化到 Durable Objects)。因此真实系统几乎从不单选。2026 年的主流架构是:入口用虚拟线程/async 处理请求,内部有状态部分用 Actor(或 Durable Objects),跨服务用消息队列衔接,批处理与 AI 推理退回进程隔离。 选型的第一问不再是"用哪个模型",而是三问:并发单元要不要跨网络?崩了要不要连坐?状态归谁? 三问的答案几乎唯一地决定边界——第一问对应消息队列/Actor 的分界线(只有这两类原生过网络,见第 5 节"分布式能力");第二问对应"进程/Actor/消息队列 vs 线程/async"的分界线(前三者的崩溃都不连坐:进程隔离于 OS 层、Actor 隔离于 supervisor、队列隔断于消息可重投,线程与 async 则共处同一地址空间,见对比表"故障隔离"行);第三问对应"状态归属"一行的分界线(Actor 独占状态、外界只能发消息,线程/async 共享一切,见对比表)。
洞察四:场景决策表
| 场景特征 | 首选 | 理由 | 次选/备注 |
|---|---|---|---|
| 单机 CPU 密集、需要利用多核且互不信任 | 进程 | 崩溃隔离 + 绕开 GIL | free-threaded Python 3.14+ 后线程开始可行 |
| 单进程内大量 I/O 并发(万~百万级等待) | async/await 或虚拟线程/goroutine | 用户态调度成本最低 | Go/Rust/Java 25/Python 3.14 均已就位 |
| 计算有状态且被并发触碰(会话、协作文档、游戏房间) | Actor(DO/Dapr/Orleans/BEAM) | 单写者串行化,免锁免竞争 | 消息队列 + 数据库乐观锁是轻量替代 |
| 服务间解耦、削峰、失败重试 | 消息队列 | 跨网络、可缓冲、可重投 | 任务子队列可先用 Postgres SKIP LOCKED(经验口径:持续每秒数千条以内、单条 KB 级,见辩论二) |
| 事件溯源、审计日志、多消费者回放 | Kafka 类日志流 | 持久、有序、可重放 | RabbitMQ/NATS 不擅长长保留 |
| 极低延迟 Pub/Sub(游戏、行情、IoT) | NATS/内存总线 | 亚毫秒 P99(方向性基准) | 需要持久化再加 JetStream |
| 长事务工作流、跨服务编排 | 队列 + 工作流引擎(Temporal/Dapr Workflow 一类) | 断点续跑需要持久状态机 | 纯 async/await 难以跨机器持久化 |
洞察五:边界不只由技术划定——治理与经济动机同样划界
两轴各给出了一个非技术性证据,交叉之后构成第三条规律。纵向证据:2022 年 Akka 转 BSL 许可把 JVM Actor 阵营劈成 Akka(付费/尊重 BSL)与 Pekko(纯开源)两轨,Play、Flink 跟随迁移——Actor 的生存形态高度依赖框架治理,选 Actor 就是选一个治理主体。横向证据:2025 年"Postgres 当队列"运动兴起,动因是省一套基础设施并与业务事务同库一致,而非性能——经济动机正在把消息队列的"任务队列"子品类让给数据库,同时把"事件流"品类留给 Kafka;同样,Durable Objects/Dapr 把 Actor 的边际成本降到"调一个 API",压缩自建集群的地盘。给选型者的提示:在技术边界之内做选择时,把许可证、治理模式与"少运维一套组件"的诱惑一起放进天平——三者都可能在未来三年改变你系统的并发模型。
对读者的判断与建议
- 默认从线程系起步(Go 的 goroutine、Java 25 的虚拟线程、Python 3.14+ free-threading)。 同步写法的可读性与调试成本最低,2026 年它已覆盖大多数"简单 I/O 并发"场景;不要为了并发而先验地把整个代码库 async 化——虚拟线程时代 JVM 生态的碎片化教训刚刚过去十年。
- 只有当语言没有运行时级轻量线程时才考虑 async/await(JS、Rust、Python<3.14),并且从第一天就用结构化并发(TaskGroup/Trio 风格)管理任务生命周期。 "运行时级轻量线程"指运行时内建"同步写法 + 运行时自动挂起"的并发原语(goroutine、虚拟线程即此类):JS 运行时只有事件循环,Rust 的 Tokio 任务须 async 显式着色,Python<3.14 的线程受 GIL 限制——故三者都算"无轻量线程"一类。事后补会付出"染色传染"的税——这是 C10K 到 Loom 之间二十年最贵的学费。
- CPU 密集、多租户、不可信代码或 AI 推理,用进程隔离。 free-threaded Python 正在侵蚀它"绕开 GIL"的地盘,但"崩溃不连坐"仍不可替代(推理基础设施的进程化设计——Ray Serve/vLLM——见第 5 节"故障隔离")。
- 有状态并发(会话、房间、协作文档、AI Agent 状态)选 Actor 形态。 能上 BEAM 就上 BEAM;JVM 上先确认 Akka/Pekko 的许可证立场;云上可用 Durable Objects/Dapr,接受厂商锁定换取零运维。
- 跨服务用消息队列,且把"任务队列"与"事件流"分开对待。 前者 Postgres/轻量队列常常够用(量级口径与迁移信号见辩论二),后者选 Kafka 类;引入队列的同时就把幂等消费写进接口契约——消息语义是队列的税。
- 永远组合使用。 不要问"我们该用哪个并发模型",要问"这一层该用哪个"。每个新模块立项时把三问各过一遍:并发单元要不要跨网络?崩了要不要连坐?状态归谁?
参考来源
以下来源合并自纵轴章与横轴章(去重后),均为 2026-11-01 检索或验证的公开来源;检索时点为 2026-11-01,其后变化未逐项核实。
历史与理论
- Hewitt/Bishop/Steiger, *A Universal Modular ACTOR Formalism for AI*, IJCAI 1973 — https://www.ijcai.org/proceedings/1973/0031.pdf
- Hoare, *Communicating Sequential Processes*, CACM 1978 — https://www.cs.cmu.edu/~crary/819-f09/Hoare78.pdf ;https://en.wikipedia.org/wiki/Communicating_sequential_processes
- Dijkstra 信号量与 THE 系统 — https://en.wikipedia.org/wiki/Semaphore_(programming) ;https://en.wikipedia.org/wiki/THE_multiprogramming_system ;https://dl.acm.org/doi/10.1145/361209.361220
- Russ Cox, *Bell Labs and CSP Threads* — https://swtch.com/~rsc/thread/
- Erlang 历史(1986/1998/OTP)— https://medium.com/fullstackhacks/erlang-no-process-was-harmed-in-the-making-of-this-post-247a80223f19 ;Armstrong 博士论文(2003)— https://erlang.org/download/armstrong_thesis_2003.pdf
- POSIX Threads — https://hpc-tutorials.llnl.gov/posix/
- C10K — http://www.kegel.com/c10k.html ;https://en.wikipedia.org/wiki/C10k_problem
- Node.js(2009)— https://www.slideshare.net/slideshow/original-slides-from-ryan-dahls-nodejs-intro-talk/71253590 ;https://www.bizjournals.com/boston/inno/stories/news/2011/01/31/nodejs-interview-4-questions-with-creator-ryan.html
- Go(2009/2012)— https://opensource.googleblog.com/2009/11/hey-ho-lets-go.html ;https://go.dev/doc/faq ;goroutine 初始栈 2KB — https://go.dev/doc/go1.4
- async/await 历史(F# 2007 / C# 5.0 2012)— https://en.wikipedia.org/wiki/Async/await ;https://dev.to/maxarshinov/a-brief-history-of-asyncawait-264j
- Python asyncio(PEP 3156 / PEP 492 / PEP 492 vs 3152)— https://peps.python.org/pep-3156/ ;https://peps.python.org/pep-0492/ ;https://lwn.net/Articles/644128/
- Kotlin 1.3 协程转正 — https://blog.jetbrains.com/kotlin/2018/10/kotlin-1-3/
- Swift 5.5(2021-09-20)— https://www.swift.org/blog/swift-5.5-released/
- 结构化并发(Trio)— https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/
- Herb Sutter, *The Free Lunch Is Over*(2005)— https://www.gotw.ca/publications/concurrency-ddj.htm
- Reactive Streams — https://www.reactive-streams.org/
运行时与语言现状(2024–2026)
- Project Loom / 虚拟线程 / 结构化并发 — https://openjdk.org/projects/loom ;https://openjdk.org/jeps/444 ;https://openjdk.org/jeps/453 ;https://openjdk.org/jeps/505 (Java 25 LTS GA 2025-09-16;JEP 525 为 JDK 26 第六预览;JEP 506 Scoped Values 定稿)
- Rust async:trait 中的 async fn 稳定 — https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits ;async-std 停更与生态整合 — https://corrode.dev/blog/async ;https://blog.jetbrains.com/rust/2026/02/17/the-evolution-of-async-rust-from-tokio-to-high-level-applications
- Python 免 GIL(PEP 703 / PEP 779)— https://docs.python.org/3/howto/free-threading-python.html ;https://peps.python.org/pep-0703/ ;https://peps.python.org/pep-0779/ ;https://py-free-threading.github.io ;https://codspeed.io/blog/state-of-python-3-13-performance-free-threading
- Erlang/OTP 28(2025-05-20,优先级消息)— https://www.erlang.org/blog/highlights-otp-28 ;Elixir 互操作与可移植性 — https://elixir-lang.org/blog/2025/08/18/interop-and-portability
- 虚拟线程生产案例汇总(2025)— https://medium.com/@gangoladeepa/java-virtual-threads-project-loom-with-code-2025-guide-fb80252c16d0
Actor 治理与虚拟 Actor
- Akka 许可变更与 Pekko 分叉 — https://www.lightbend.com/blog/why-we-are-changing-the-license-for-akka ;https://blog.lunatech.com/posts/2023-10-27-akka-licence-change-one-year-later ;https://akka.io/bsl-license-faq
- 虚拟 Actor:MSR Orleans 论文(2014);Dapr — https://dapr.io/ ;Cloudflare Durable Objects — https://developers.cloudflare.com/durable-objects/ ;https://blog.cloudflare.com/durable-objects-zero-latency-sqlite-storage/
消息队列与事件流
- 消息队列演进(IBM MQ 1993 / JMS 1998 / RabbitMQ 2007 / Kafka 2011 / Pulsar 2016)— https://en.wikipedia.org/wiki/IBM_MQ ;https://bytebytego.com/guides/how-do-message-queue-architectures-evolve ;https://www.rabbitmq.com/tutorials/amqp-concepts ;https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol
- Kafka 4.0 去 ZooKeeper(2025-03,KRaft 唯一模式)— https://kafka.apache.org/ ;https://www.confluent.io/learn/zookeeper-kafka
- 中间件基准(方向性参考,高度依赖配置)— https://onidel.com/blog/nats-jetstream-rabbitmq-kafka-2025-benchmarks ;https://medium.com/@kanishks772/kafka-vs-rabbitmq-vs-nats-which-messaging-system-should-you-use-and-why-fdd0b68ec45c
争论与叙事
- 函数着色争论 — https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ ;https://news.ycombinator.com/item?id=41898204
- 虚拟线程 vs 响应式 — https://stackoverflow.com/questions/78318131/do-java-21-virtual-threads-address-the-main-reason-to-switch-to-reactive-single ;https://www.reddit.com/r/java/comments/1hjjcb4/are_virtual_threads_making_reactive_programming
- Postgres 当队列:Prisma 博客 *You Don't Need a Job Queue — Postgres Already Has SKIP LOCKED*(含 worker 崩溃恢复、单消费者语义、fan-out、vacuum 吞吐上限等局限的正面自述)— https://www.prisma.io/blog/you-dont-need-a-job-queue-postgres-already-has-skip-locked ;HN "On SQS" 讨论串(2019-05,343 分 225 评论,Postgres SKIP LOCKED 与 SQS 之争及崩溃语义辩论)— https://news.ycombinator.com/item?id=20019874
- OS 线程默认栈 — https://man7.org/linux/man-pages/man3/pthread_create.3.html
AI 推理与进程隔离
- Ray Serve 架构(replica 为独立 actor 进程、Controller 检测到崩溃即替换)— https://docs.ray.io/en/latest/serve/architecture.html ;vLLM V1 多进程架构(API 前端与 EngineCore 分离、ZeroMQ IPC)— https://vllm.ai/blog/2025-01-27-v1-alpha-release