并发编程(十一):Shared Memory 与 Message Passing——关系与组合使用

目录

  • [0. 从上一篇继续](#0. 从上一篇继续)
  • [1. 两种协作模型是什么关系?](#1. 两种协作模型是什么关系?)
  • [2. 两种模型如何组合使用?](#2. 两种模型如何组合使用?)
  • [3. 小结](#3. 小结)

0. 从上一篇继续

前面的文章已经分别讨论了 Shared Memory 和 Message Passing 两种并发协作模型。

接下来,我们讨论它们的关系,以及面对具体的并发需求时,如何选择与组合使用。

1. 两种协作模型是什么关系?

Shared Memory 通过直接访问共享状态进行协作,Message Passing 则通过发送和接收消息进行协作。两者的协作方式不同,却并不互斥。

上一篇分析 BlockingQueue 和 Channel 时已经看到:Producer 和 Consumer 虽然通过消息交换数据,通信组件内部仍然需要通过共享内存维护队列状态。

以 Java ArrayBlockingQueue 为例:

text 复制代码
Producer
    |
    v
ArrayBlockingQueue
    |
    +-- Array:存放消息
    |
    +-- ReentrantLock:保护队列状态
    |
    +-- Condition:队列为空或已满时等待
    |
    v
Consumer

多个 Producer 和 Consumer 可能同时入队或出队,因此 ArrayBlockingQueue 使用锁保护队列操作,并通过 Condition 协调队列空、满时的等待与唤醒。

Go Channel 通过 Runtime 的 hchan 管理缓冲区和等待队列,并使用锁、原子操作及 Goroutine 调度机制协调通信;CPython 的 queue.Queue 则使用 Mutex 和 Condition 保护队列状态。

Message Passing 没有消除底层的共享状态,而是将消息交接所需的同步封装在 Channel、Queue 等通信组件中。 这也是为什么上一篇最终仍要回到 Mutex、Atomic、Condition 和内存顺序,解释消息交接如何满足 Atomicity、Visibility 和 Ordering。

2. 两种模型如何组合使用?

在实际程序中,两种模型经常一起使用。

例如,一个任务处理系统需要由多个 Producer 提交任务、多个 Worker 并行执行,并统计成功和失败的任务数量:

text 复制代码
Producer A ──┐
             ├──> Task Queue ──> Worker 1 ──┐
Producer B ──┘                └─> Worker 2 ──┤
                                            |
                                            v
                                     Shared Statistics
                                     (Mutex / Atomic)

这里,Message Passing 负责任务交接 :Producer 将任务放入 Queue,Worker 取出并处理;Shared Memory 负责统计状态的更新:多个 Worker 通过 Mutex 或 Atomic 协调对统计数据的访问。

如果希望统计状态由一个执行单元集中维护,也可以让 Worker 发送处理结果:

text 复制代码
Producer --> Task Queue --> Workers
                               |
                               v
                          Result Queue
                               |
                               v
                          Stats Owner
                               |
                               v
                           Statistics

这时只有 Stats Owner 修改统计数据,Worker 不再直接访问它。

两种方案都能保证正确性,但各有代价:直接更新共享状态更简单;由 Owner 集中处理,便于统一维护多个字段及其约束,却增加了消息交接和 Owner 的生命周期管理,也可能形成串行瓶颈。

3. 小结

本篇从底层实现和实际应用两个角度,讨论了 Shared Memory 与 Message Passing 的关系,以及如何组合使用这两种协作模型。

下一篇进入本系列的终章。我们将回顾整个并发编程系列,并最后做个总结。


本文首发于 ThinkerQAQ 的个人博客,由作者本人同步发布。原文可能持续修订,最新版本请以个人博客为准。

相关推荐
聪明的笨猪猪6 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
用户956256254296 小时前
跟做:Firecrawl 单页转 Markdown 再丢进沙箱总结
python·api
愿有所息6 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
威哥爱编程6 小时前
模拟面试智能体开发实战 02:回答等半分钟,谁还愿意练?把服务端和流式输出搭起来
python·agent·harmonyos
秋饼6 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
威哥爱编程6 小时前
模拟面试智能体开发实战 01:面试挂了都不知道挂在哪?用 Python 给自己造一个随叫随到的 AI 面试官
python·agent·harmonyos
2601_967980117 小时前
Python行情API量化 02:顺序拉 20 要 2 秒?并发批量把时间压到 0.5 秒
python·api·限流·并发·量化
伞伞悦读7 小时前
【第81期】特征缩放怎么选:标准化、MinMax 与 Robust 的原理、泄漏对照和完整实验
人工智能·python·深度学习·机器学习
新*7 小时前
从一段 WebSocket 代码聊开:bytes 不是 zlib、binaryType 决定一切,15MB 大消息的零拷贝优化
javascript·python·websocket·性能优化·blob·zlib·arraybuffer
SL_staff7 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈