并发编程(十一):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 的个人博客,由作者本人同步发布。原文可能持续修订,最新版本请以个人博客为准。

相关推荐
用户8356290780511 小时前
如何使用 Python 对 Excel 数据进行分组和分级显示
后端·python
vx_Biye_Design2 小时前
django房屋租赁推荐系统78455-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·django·课程设计·express
齐 飞2 小时前
idea中git误操作撤回git reset --hard ‘HEAD@{1}‘
java·git·intellij-idea
你的保护色2 小时前
多AS互联BGP4综合实验设计
java
某不知名網友2 小时前
ROS2进阶:服务通信、自定义SRV接口、参数服务器与基于服务的参数通信详解
java·开发语言·microsoft
Wang's Blog2 小时前
Java 项目实战: 交友项目 - MongoDB 入门:体系结构和命令行操作
java·mongodb·交友
小溪学编程3 小时前
将 Bean 放入 Spring 容器中的方式有哪些?
java·后端·spring
java_nnnn3 小时前
LeetCode刷题滑动窗口(水果成篮+找到字符串中所有字母的异位词)Java
java·开发语言·leetcode
用户3721574261353 小时前
Java 读取、修改和清空 PDF 文档属性(元数据)
java