题记与说明
- 主题:并发与异步编程:线程、进程、async/await、Actor、消息队列的区别(主题研究)
- 精读日期:2026-11-03
- 方法:先检索(WebSearch 3 轮),后精读 5 个来源(2 个全文逐字抓取:Akka 官方文档《Actor Systems》、Microsoft Learn《Asynchronous programming - C#》;3 个深度提取:AWS《What is a Message Queue?》、Real Python《Async IO in Python: A Complete Walkthrough》、Chris Kiehl《Parallelism in One Line》),交叉比对五种并发模型的调度方式、状态归属、通信机制、失败模型与适用边界
- 来源清单(5 个,均已精读):
- Akka 官方文档:*Actor Systems*(Actor 模型第一手表述)— https://doc.akka.io/libraries/akka/current/general/actor-systems.html
- Microsoft Learn(作者 Bill Wagner,2025-03-10 更新):*Asynchronous programming - C# / The Task asynchronous programming model* — https://learn.microsoft.com/en-us/dotnet/csharp/asynchronous-programming/
- Real Python:*Async IO in Python: A Complete Walkthrough* — https://realpython.com/async-io-python/
- AWS 官方:*What is a Message Queue?* — https://aws.amazon.com/message-queue/
- Chris Kiehl(2015-05-13):*Parallelism in One Line* — https://chriskiehl.com/article/parallelism-in-one-line
- 检索辅助来源(来自 3 轮 WebSearch 摘要,未逐字精读,仅作旁证):Stack Overflow《Basics: Threaded vs Reactive concurrency model》、Medium 上 Swift/Python 并发对比文若干(见文末链接列表)
一、七维提取
1. 核心问题:这份"主题"在解决什么问题
五种东西经常被并排摆在"并发"这个词下,但它们其实分属三个不同层次,回答的是三个不同的问题:
- 线程 / 进程 / async-await 回答:"在一个程序里,谁来执行、怎么等待?" —— 这是调度层问题。三者的差别在于:谁持有状态(共享内存 vs 各自隔离)、谁负责切换(OS 抢占式 vs 程序员协作式)、一次上下文切换的代价。
- Actor 回答:"当状态必须存在、又必须被并发访问时,怎么不写锁?" —— 这是状态归属层问题。它把"可变状态 + 行为"封装进一个只能通过邮箱收消息的单元,用消息传递替代共享内存。
- 消息队列回答:"当发送方和接收方连'同时活着'都无法保证时,怎么通信?" —— 这是系统解耦层问题。它引入一个持久化的中间人(broker),把通信从函数调用变成存储转发。
所以"区别"不是一个五选一的选择题:前三个是互斥的执行模型(一个进程内总要选一种主模型),Actor 与消息队列是建立在前三者之上的通信与架构模式。真正的决策链是"先定执行模型,再定通信模型"。
2. 主要主张(各来源的核心论点)
| 来源 | 核心主张 | 原文关键句(摘引) |
|---|---|---|
| Akka《Actor Systems》 | Actor 是"最严格的面向对象":状态与行为封装,只通过邮箱通信;用层级与监督处理失败,而不是防御式编程 | "Actors are objects which encapsulate state and behavior, they communicate exclusively by exchanging messages which are placed into the recipient's mailbox." / "tasks are split up and delegated until they become small enough to be handled in one piece" |
| Microsoft Learn《Asynchronous programming》 | async/await 的目标是"读起来像顺序语句,执行顺序却复杂得多";await 暂停的是方法,不是线程;异步≠并行(一个人做早餐 vs 多个厨师) | "The goal of task asynchronous programming is to enable code that reads like a sequence of statements, but executes in a more complicated order." / "Cooking breakfast is a good example of asynchronous work that isn't parallel. One person (or thread) can handle all the tasks." |
| Real Python《Async IO》 | asyncio 是单线程、单进程的协作式多任务;对 I/O 密集任务优于线程(无线程管理开销),但对 CPU 密集毫无用处;线程是"有限资源",协程不是 | "Creating thousands of threads will fail on many machines or can slow down your code." / "Only use async def functions for non-blocking operations." |
| AWS《What is a Message Queue?》 | 消息队列是"服务到服务的异步通信":broker 是临时存消息的轻量缓冲,一条消息只被一个消费者处理一次(点对点),广播要叠加 Pub/Sub | "Messages are stored on the queue until they are processed and deleted." / "Many producers and consumers can use the queue, but each message is processed only once, by a single consumer." |
| Chris Kiehl《Parallelism in One Line》 | Python 并行问题更多是教学问题而非技术问题:CPU 密集用 multiprocessing.Pool.map,I/O 密集用 multiprocessing.dummy(线程版),一行 map 注入并行 | "If you run too many threads, you'll waste more time switching between them than doing useful work." / "Map is a cool little function, and the key to easily injecting parallelism into your Python code." |
3. 方法:这个主题的"论证方式"是什么
- 官方文档走"模型定义"路线:Akka 直接给出 Actor 的心智模型("把他们当人看,分派子任务,想清楚失败如何上报"),AWS 给出 broker 的角色定义(producer/buffer/consumer)。这类论证靠概念边界清晰,不靠跑分。
- 教程走"反例+数字"路线:Microsoft 用"做早餐"贯穿——同步 30 分钟 → 只改 await 不并发仍 30 分钟 → 先启动后 await 压到 20 分钟 →
Task.WhenAny监控式并发 15 分钟,用同一例子逐级演进;Real Python 引用 Miguel Grinberg 的"国际象棋车轮战"类比给出具体计算(同步 12 小时 vs 异步 1 小时);Chris Kiehl 给出缩略图任务 27.9s → 5.6s 的实测。 - 共性:五类模型没有谁"更快"的绝对答案,所有来源都用任务类型(I/O-bound vs CPU-bound)× 并发规模 × 状态归属三个轴来定位,而非单维性能排序。
4. 数据与证据
- Async vs 同步(Real Python,转引 Grinberg 车轮战):24 个对手、每步思考 5 秒、对手 55 秒。同步(下完一个再下一个):(55+5)×30×24 = 12 小时;异步(走完一步立刻去下一桌):24×5=120 秒/轮 → 共 1 小时。配套代码实测:count 脚本同步 6.03s vs 异步 2.00s。
- Async vs 同步(Microsoft 早餐例):同步串行 ≈30 分钟;加了 await 但仍是"启动即等待" ≈ 相同;先启动三个任务、最后统一 await ≈20 分钟;
Task.WhenAny循环监控 ≈15 分钟。 - 多进程加速(Chris Kiehl):~6000 张图片缩略图,串行 27.9s →
Pool5.6s;URL 抓取用 4 线程池有效,但池大小 >9 后收益递减——"开太多线程,切换开销会吃掉有用功"。 - 规模量级(Akka):一个 ActorSystem 内可容纳百万级 actor,单个 actor 开销"约 300 字节";ActorSystem 本身是重量级结构(分配 1..N 个线程),每应用建一个。线程 vs 协程量级对照(Real Python):数千线程"在很多机器上会失败或显著拖慢",数千乃至更多协程可行。
- 消息队列语义(AWS):消息存到被处理并删除为止;每条消息只被一个消费者处理一次;主要收益三条——解耦重处理、缓冲/批处理、削峰。
5. 结论:一张坐标系里的五个位置
| 维度 | 进程 | 线程 | async/await | Actor | 消息队列 |
|---|---|---|---|---|---|
| 谁在调度 | OS(抢占式) | OS(抢占式) | 事件循环(协作式,await 是让位点) | Actor 运行时(邮箱驱动,底层仍是线程/事件循环) | Broker(存储转发,不调度计算) |
| 状态归属 | 独立地址空间,天然隔离 | 全体共享,必须加锁 | 单线程内共享,await 点是竞争窗口 | Actor 私有,外部不可见("不要在 actor 间传可变对象") | 无状态;状态随消息走或落在下游服务 |
| 通信机制 | 管道/共享内存/socket | 共享内存+锁 | 对象引用/Future | 不可变消息 → 邮箱(ask/tell) | 持久化消息 → 队列(至多一次/至少一次) |
| 单位成本 | 最高(fork + IPC) | 中(MB 级栈) | 极低(KB 级协程帧) | 低(~300 字节/actor) | 最高(跨网络,持久化+确认) |
| 失败影响 | 一进程崩不影响其他 | 一线程崩可能拖垮整个进程 | 协程异常需在 await 处捕获(.NET:异常存入 Task,await 时重抛) | 失败被监督者捕获,"错误内核"模式:把危险子任务下放给子 actor | 消费失败可重试/进死信;生产者消费者互不拖垮 |
| 典型量级 | ~CPU 核数 | 数百 | 数万–数十万 | 百万级 | 跨服务,百万级消息/天 |
| 最佳场景 | CPU 密集并行、故障隔离 | 调阻塞库(不 await 的 DB 驱动等)、中等并发 I/O | 单机高并发 I/O(爬虫、网关、SSE/WS) | 有状态并发、容错分布式(游戏、IM、交易) | 服务解耦、削峰、跨机/跨语言任务分发 |
决策链(本次精读的收敛结论):
- 任务是 CPU 密集?→ 进程池(
multiprocessing.Pool),核数= worker 数。 - I/O 密集但依赖阻塞库、并发 <1k?→ 线程池。
- I/O 密集、全链路可用非阻塞库、并发上万?→ async/await(记住:链上任何一个阻塞调用"stops everything in its tracks")。
- 并发单元持有可变状态且要容错?→ Actor(用消息传递消灭锁)。
- 发送方和接收方不在一个进程/一台机器,或需要削峰重试?→ 消息队列。
- 它们是可组合的:async 进程内处理 I/O + 进程池跑 CPU + 队列连接服务 + Actor 管有状态会话,是生产系统的常见组合,不是单选题。
6. 局限(来源承认的 + 本次研究推断的)
- async/await 不是并发的万能药:Real Python 明确 async 与 multiprocessing "不是真正的竞赛",CPU 密集仍归多进程;且 asyncio 对"忘记 await"的协程只是创建对象不执行("Coroutines don't do much on their own until they're tied to the event loop")。
- Actor 模型的代价在 Akka 文档里说得很克制但存在:消息顺序在大型系统里不可控("the exact order in which messages are processed in large systems is not controllable");阻塞会占死线程,需要专门管理("Blocking Needs Careful Management");Scala 闭包传行为有意外共享可变状态的风险。
- 消息队列的"每条消息恰好一个消费者"是双刃剑(AWS):需要广播时必须额外叠加 Pub/Sub;本次来源未展开投递语义(at-least-once 造成的重复消费幂等问题)——参照既有笔记《事件驱动系统与消息队列》(2026-10-31) 的结论:这正是 Kafka(重放日志)与 RabbitMQ(per-message 确认)的分野所在。
- 线程并非过时:Real Python/搜索来源均提示,在 Python 里若依赖库是阻塞的(大量 DB 驱动、SDK),线程池仍是正确答案;GIL 只惩罚 CPU 并行,不惩罚 I/O 等待。
- 本次研究的局限:五个来源覆盖 Python/.NET/JVM 三生态,但对 Go 的 goroutine/CSP、Rust 的 async 运行时、Erlang 原生 actor 未做第一手精读;Actor 部分仅有 Akka 单一来源(概念表述权威,但缺 Erlang/Akka 演进对照);所有性能数字均转引自各来源的示例或实测,未在本机复跑。
7. 对我的启发
- 写代码时的默认决策树值得贴在墙上:CPU 密集→进程;阻塞库→线程;非阻塞 I/O 高并发→async;有状态并发→Actor;跨服务→MQ。以前容易把"用 async"当成"更快"的默认选择——本次精读的证据链明确否定了这一点:async 的收益来自"等待时不占资源",与算得快无关。
- "await 暂停方法不暂停线程"是这个主题最值得写透的一个反直觉点(Microsoft 早餐例的四段式演进是绝佳的教学素材,可直接化用到博客:同步 30 分钟 → 15 分钟的四个 commit)。
- Actor 与消息队列的边界:邮箱≈每 actor 一个私有队列,但 Actor 多了"状态封装+监督重启",MQ 多了"持久化+跨机路由"。如果有人问"Actor 是不是就是一个内存版 MQ",答案是:通信面像,失败处理面完全不同。
- 与我既有笔记的衔接:《事件驱动系统与消息队列》(2026-10-31, knowledge-fexxiou8-0) 已覆盖 MQ 内部选型(Kafka/RabbitMQ/NATS/Redis Streams),本次笔记补齐了它上游的"为什么需要队列"——五层模型中 MQ 位于架构层;两篇合起来构成"执行模型 → 通信模型 → 具体中间件"的完整决策链。
- 个人项目落地:批量 LLM API 调用/爬虫 →
asyncio+ 信号量限流;本地批量转码缩略图 →ProcessPoolExecutor.map(Chris Kiehl 的"一行 map"模式);若写 agent 常驻服务且每个会话有状态 → actor 风格(每会话一个状态对象,消息驱动),跨进程再上队列。
二、逐来源精读摘录(原文引述)
来源 1:Akka 官方文档《Actor Systems》(全文精读)
"Actors are objects which encapsulate state and behavior, they communicate exclusively by exchanging messages which are placed into the recipient's mailbox. In a sense, actors are the most stringent form of object-oriented programming, but it serves better to view them as persons: while modeling a solution with actors, envision a group of people and assign sub-tasks to them, arrange their functions into an organizational structure and think about how to escalate failure."
要点:
- 层级结构是 Actor 系统的"quintessential feature":任务不断拆分下放,直到小到可以一口气处理;失败也沿层级上报(escalate),对比"分层软件设计容易退化成不漏出任何失败的防御式编程"。
- 错误内核模式(Error Kernel Pattern,源自 Erlang):持有关键数据的 actor 把危险子任务交给子 actor,自己做失败处理;每个请求可新建子 actor 以简化回复收集。
- 四条最佳实践:actor 不应阻塞("Actors should not block ... on some external entity—which might be a lock, a network socket, etc.—unless it is unavoidable");不在 actor 间传可变对象("Do not pass mutable objects between actors. ... prefer immutable messages"——一旦破坏封装"you are back in normal Java concurrency land with all the drawbacks");不在消息里传行为;顶层 guardian 只负责启动子系统。
- 量级:"There may be millions of actors within one such system ... they weigh in at an overhead of only roughly 300 bytes per instance.";ActorSystem 重量级,每逻辑应用一个。
来源 2:Microsoft Learn《Asynchronous programming - C#》(全文精读)
"Cooking breakfast is a good example of asynchronous work that isn't parallel. One person (or thread) can handle all the tasks. One person can make breakfast asynchronously by starting the next task before the previous task completes. ... For a parallel algorithm, you need multiple people who cook (or multiple threads)."
要点:
- 异步 vs 并行的定义级区分:异步 = 一人(一线程)不等上一步就开始下一步;并行 = 多人各管一摊、各自阻塞等待。
- await 的语义:"The
awaitkeyword provides a nonblocking way to start a task, then continue execution when the task completes.";同步等待相当于"盯着面包机直到面包弹出"。 - 组合律:"if any portion of an operation is asynchronous, the entire operation is asynchronous."(异步操作后接同步工作 = 整体仍是异步操作)
- 异常模型:异步方法抛出的异常存入
Task.Exception(AggregateException),在await处重抛第一个内部异常——"Extracting the first inner exception makes working with asynchronous methods as similar as possible to working with their synchronous counterparts." - 演进数字:同步 ≈30 min → await 化但串行 ≈30 min → 先启动后 await ≈20 min →
Task.WhenAny监控 ≈15 min。 - 编译器机制:await 不是翻译成
ContinueWith调用,而是"the compiler generates optimized state machine code",链式回调即"monadic do-notation"。
来源 3:Real Python《Async IO in Python》(深度提取精读)
"Asynchronous routines can pause their execution while waiting for a result and allow other routines to run in the meantime." / "Creating thousands of threads will fail on many machines or can slow down your code."
要点:
- asyncio = 单线程单进程的协作式多任务,"more closely aligned with threading than with multiprocessing, but it's different from both"。
- 事件循环:"an infinite loop that monitors coroutines, tracks idle tasks, and wakes them when awaited results are available";默认一个 loop、一个线程、一个核;可插拔(uvloop 更快)。
- 规模论证:线程是有限系统资源,数千线程会失败;协程帧 KB 级,可上万——这是 asyncio 相对线程的本质优势(等待成本),不是执行速度。
- 红线:"Only use async def functions for non-blocking operations.";在 async 里调
time.sleep()一类阻塞调用会"stops everything in its tracks"。 - 车轮战数字:同步 12 小时 vs 异步 1 小时;count 脚本 6.03s vs 2.00s。
来源 4:AWS《What is a Message Queue?》(深度提取精读)
"A message queue is a form of asynchronous service-to-service communication used in serverless and microservices architectures." / "Many producers and consumers can use the queue, but each message is processed only once, by a single consumer."
要点:
- 三角色:producer 入队;queue 是"a lightweight buffer which temporarily stores messages";consumer 取出并处理。
- 点对点语义:多生产者多消费者共享一个队列时,每条消息仍只被一个消费者处理一次(天然负载均衡);要广播须叠加 Pub/Sub。
- 三大收益:"Message queues can be used to decouple heavyweight processing, to buffer or batch work, and to smooth spiky workloads."
- 与同步 RPC 的本质差异:发送方与接收方无需同时在线,broker 持有消息直到消费者就绪。
来源 5:Chris Kiehl《Parallelism in One Line》(深度提取精读)
"If you run too many threads, you'll waste more time switching between them than doing useful work." / "Map is a cool little function, and the key to easily injecting parallelism into your Python code."
要点:
multiprocessing与multiprocessing.dummy(线程版)API 完全同构——"an exact clone of the multiprocessing module","So anything that applies to one, applies to the other"——切换执行模型只需换 import,这是该文最重要的工程洞见。- 实测:~6000 张缩略图串行 27.9s →
Pool5.6s;URL 抓取线程池 4 线程有效,>9 收益递减。 Pool()默认每核一个 worker;pool.map(func, iterable)把 40 行线程池样板压成 ~4 行。
三、检索与验证记录(本会话实际执行)
- WebSearch×3:
thread vs process vs async await vs actor model vs message queue comparison concurrency(返回 8 条结果与对比表);chriskiehl.com "Parallelism in Python" article threads processes GIL(确认正确 URL 为 parallelism-in-one-line);Microsoft learn async await task asynchronous programming model C# depth state machine+ AWS/IBM MQ 页面定位。 - 全文抓取:web_reader 抓取 Akka《Actor Systems》成功(返回全文);Microsoft Learn《Asynchronous-programming》与 Chris Kiehl、AWS MQ、Real Python 经 WebFetch 抓取(前两者返回全文/详摘)。
- 抓取失败记录:EIP《MessageQueue》页(enterpriseintegrationpatterns.com/patterns/messaging/MessageQueue.html)HTTP 404(WebFetch 两次),已用 AWS 官方 MQ 页替代为 MQ 来源;
chriskiehl.com/article/parallelism-in-python404,实际标题为《Parallelism in One Line》。
四、相关链接(检索所得、旁证级)
- Stack Overflow: Basics: Threaded vs Reactive concurrency model — https://stackoverflow.com/questions/29067704/basics-threaded-vs-reactive-concurrency-model
- Medium: Parallelism, concurrency and async/await — https://medium.com/@N1Dovud/a-little-bit-about-parallelism-concurrency-and-async-await-19355143cc09
- Medium: Python Concurrency for Production Systems — https://medium.com/@sivakami.kanda/python-concurrency-for-production-systems-the-key-guide-to-processes-threads-and-async-i-o-9609a21ae3be
- IBM: What Is a Message Queue? — https://www.ibm.com/think/topics/message-queues
- AWS: Message Queue Features — https://aws.amazon.com/message-queue/features/
- Reddit r/learnpython: Multithreading, Multiprocessing or async — https://www.reddit.com/r/learnpython/comments/1fvsf89/multithreading_multiprocessing_or_async
*精读方法说明:本笔记基于 5 个已精读来源 + 3 轮检索摘要撰写;所有引述均来自上述链接页面(全文抓取或深度提取)。未在本机复跑任何性能数字;Go/Rust/Erlang 生态未做第一手精读,已在局限中标注。*