不能直接用 multiprocessing.queue 传大 numpy 数组,因为其依赖 pickle 序列化会导致完整内存拷贝和高开销;应改用 multiprocessing.shared_memory 实现零拷贝共享,但需手动同步 dtype、shape 等元信息,并严格管理生命周期与字节序。

为什么不能直接用 multiprocessing.Queue 传大 NumPy 数组
因为 Queue 底层靠 pickle 序列化,大数组会触发完整内存拷贝 + 序列化开销,实测 500MB 数组传输耗时可能超 2 秒,且内存峰值翻倍。这不是“慢一点”,而是根本不可扩展。
真正要的是零拷贝共享:子进程直接读写同一块物理内存页。Python 3.8+ 的 multiprocessing.shared_memory 模块就是为此设计的,但 NumPy 需要手动桥接。
关键限制:shared_memory.SharedMemory 只管分配裸字节缓冲区,不理解 NumPy 的 dtype、shape、strides —— 必须自己把数组元信息同步过去。
如何创建并映射共享内存给 NumPy 数组
核心是两步:先用 SharedMemory 分配缓冲区,再用 np.ndarray 构造函数把这块缓冲区“解释”成带 shape/dtype 的数组。
- 主进程创建共享内存:
shm = SharedMemory(create=True, size=arr.nbytes),注意size必须等于arr.nbytes(不是arr.size) - 用
np.ndarray绑定缓冲区:shared_arr = np.ndarray(arr.shape, dtype=arr.dtype, buffer=shm.buf) - 必须立刻复制数据:
shared_arr[:] = arr[:](切片赋值,避免重新分配) - 子进程中需用相同
name附加:shm = SharedMemory(name="your_name"),再同样构造ndarray
示例片段:
from multiprocessing import shared_memory import numpy as np <h1>主进程</h1><p>arr = np.random.random((1000, 1000)).astype(np.float32) shm = shared_memory.SharedMemory(create=True, size=arr.nbytes) shared_arr = np.ndarray(arr.shape, dtype=arr.dtype, buffer=shm.buf) shared_arr[:] = arr[:] # 触发实际拷贝</p><h1>子进程里(收到 shm.name 后)</h1><p>shm_child = shared_memory.SharedMemory(name=shm.name) arr_child = np.ndarray(arr.shape, dtype=arr.dtype, buffer=shm_child.buf)</p><h1>此时 arr_child 和 shared_arr 共享底层内存</h1>
dtype 和字节序不一致会导致静默错误
NumPy 数组的 dtype 决定了每个元素占多少字节、如何解释比特位。如果主进程用 np.float64 写,子进程用 np.float32 读,不会报错,但数值全乱 —— 因为读取时每 4 字节被当做一个 float32,而原数据是每 8 字节一个 float64。
同样,big-endian 和 little-endian 在跨平台时(比如 x86 主机 vs ARM 设备)必须显式对齐。建议统一用 arr.astype(arr.dtype.newbyteorder('='), copy=False) 确保本地字节序。
- 永远显式传递
shape、dtype、nbytes给子进程,不要依赖“约定” - 用
arr.dtype == other_arr.dtype校验,别只比字符串str(arr.dtype) - 整数类型尤其危险:
int32和uint32内存布局相同但语义不同,NumPy 不阻止你强行 reinterpret
共享内存生命周期管理容易漏掉 unlink
SharedMemory 对象本身不自动释放系统资源。即使所有 Python 引用消失,shm.buf 对应的 POSIX 共享内存段(Linux/macOS)或内存映射文件(Windows)仍驻留,直到系统重启或手动清理。
必须由创建者(通常是主进程)在确认所有子进程退出后调用 shm.unlink();子进程只需调用 shm.close()(关闭当前进程的句柄)。
- 忘记
unlink()→/dev/shm/xxx文件堆积,df -h /dev/shm会爆满 - 过早
unlink()→ 子进程shm.buf访问触发Bus error或静默数据损坏 - 推荐模式:主进程用
atexit.register(shm.unlink)+ 显式wait()子进程结束后再unlink()
复杂点在于:多个子进程可能异步读写,得用 multiprocessing.Semaphore 或 Event 协调访问时机,共享内存本身不提供同步机制。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











