Python 进程间通信的常见实现方案

15次阅读

multiprocessing.queue 最省心但需主进程创建后传递给子进程;pipe 更快但仅限双端通信;共享内存最快但需手动同步且限基础类型;切勿混用线程与进程同步原语。

Python 进程间通信的常见实现方案

multiprocessing.Queue 传数据最省心,但别在多进程里反复创建

它底层自动处理序列化和锁,适合传递小到中等体积的对象(比如 dictlist、基本类型)。常见错误是每个子进程都新建一个 Queue 实例——这会导致通信通道完全隔离,根本收不到消息。

  • 必须由主进程创建后作为参数传给子进程,或通过继承 multiprocessing.Process 的方式共享
  • 不支持跨机器、不支持非 multiprocessing 启动的进程(比如 os.fork() 或线程)
  • 如果频繁塞入大对象(如上 MB 的 numpy 数组),性能会明显下降,因为要 pickle/unpickle
  • 示例:主进程 q = multiprocessing.Queue(),子进程中用 q.put("done"),主进程用 q.get() 接收

multiprocessing.Pipe 比 Queue 快,但只支持两个端点双向通信

它绕过序列化层(默认只用字节流),吞吐更高,适合高频、低延迟的小数据交换。但它的设计就是一写一读配对,强行多路复用容易出错。

  • 返回一对连接对象:parent_connchild_conn,各自只能调用 send()/recv()
  • 不能像 Queue 那样被多个进程同时 put,也不支持广播
  • 如果一端关闭而另一端还在 recv(),会抛 EOFError;没及时 recv 积压太多,可能触发系统缓冲区满导致阻塞
  • 注意:Pipe(duplex=False) 变成单向,适合明确生产者 / 消费者角色的场景

共享内存(multiprocessing.Array / multiprocessing.Value)适合高频读写固定结构数据

绕过序列化和内核拷贝,直接操作同一块物理内存,速度最快。但它只支持基础类型(c_intc_doublec_char 等),也不能自动同步。

  • Value 存单个值,Array 存同类型数组;二者都需显式指定 ctypes 类型,比如 Array('i', [1,2,3]) 中的 'i' 是 int
  • 没有内置锁,多个进程同时写会脏数据;必须配合 multiprocessing.Lock 使用
  • 不能存 Python 对象(如 strdict),Array('c', b"hello") 是可行的,但 Array('c', "hello") 会报错
  • 适用于计数器、状态标志、预分配缓冲区等场景,不适合动态结构数据

别碰 threading 相关机制来搞进程通信

queue.Queuethreading.Eventthreading.Lock 这些只在线程间有效。在多进程里直接用它们,程序不会报错,但行为完全不可预测——比如 Event.wait() 永远不返回,Lock 完全不起作用。

立即学习 Python 免费学习笔记(深入)”;

  • 所有跨进程同步原语都必须来自 multiprocessing 模块,哪怕名字一样(如 multiprocessing.Lock
  • 混淆的典型表现:主进程设了 threading.Event,子进程调 wait() 却卡死——因为事件状态没被 fork 继承,也没跨进程同步机制
  • 如果你在用 concurrent.futures.ProcessPoolExecutor,它内部封装了 Queue,此时更不该手动引入线程级同步工具

真正麻烦的是混合使用多种机制时的生命周期管理:比如 Pipe 的连接对象没 close,可能卡住进程退出;Array 分配的内存没被回收,可能残留共享段。这些细节不报错,但会让程序在高负载或长时间运行后突然异常。

text=ZqhQzanResources