应优先使用sharedmemory+numpy.ndarray实现图像共享,因其支持显式内存控制和安全数组映射;multiprocessing.array因仅限一维、无对齐保障且易越界而不适用。

直接用 multiprocessing.Array 或 multiprocessing.shared_memory.SharedMemory 做图像预处理共享,90% 的人会卡在内存对齐或进程同步上——不是共享失败,就是读出乱码或崩溃。核心问题不在“能不能共享”,而在“怎么让多个进程安全、零拷贝地访问同一块图像数据”。
为什么不能直接用 multiprocessing.Array 存整张图
multiprocessing.Array 只支持一维基础类型(如 'B' 表示 uint8),而图像本质是三维数组(H×W×C)或二维(灰度图)。强行 flatten 再 reshape 容易出错:
- 不同进程 reshape 时若维度不一致(比如一个用
(128,128),另一个误写成(128,128,1)),结果数据错位但不报错 - Array 不自动管理字节序和内存对齐,OpenCV 或 PIL 读取时可能触发
ValueError: buffer is not C-contiguous - 单个 Array 最大长度受系统限制(Linux 默认约 2GB),处理 4K 图像批量时极易越界
SharedMemory + numpy.ndarray 是更稳的选择
Python 3.8+ 的 multiprocessing.shared_memory.SharedMemory 允许你显式控制内存块大小和生命周期,配合 numpy.ndarray 的 buffer 参数可安全映射为图像数组:
- 先计算所需字节数:
size = height * width * channels * np.dtype('uint8').itemsize - 主进程创建共享内存:
shm = SharedMemory(create=True, size=size, name='img_buffer') - 用
np.ndarray(shape, dtype, buffer=shm.buf)构造视图,所有子进程复用同一name打开即可 - 务必在所有进程结束后手动调用
shm.close()和shm.unlink(),否则内存泄漏
示例关键片段:
import numpy as np from multiprocessing import Process, shared_memory from PIL import Image <p>def worker(shm_name, shape): existing_shm = shared_memory.SharedMemory(name=shm_name) img_array = np.ndarray(shape, dtype=np.uint8, buffer=existing_shm.buf)</p><h1>此处做 resize / convert 等操作,直接修改 img_array 即写入共享内存</h1><pre class="brush:python;toolbar:false;">existing_shm.close()
主进程
shape = (128, 128) size = int(np.prod(shape)) # 灰度图 shm = shared_memory.SharedMemory(create=True, size=size) img_view = np.ndarray(shape, dtype=np.uint8, buffer=shm.buf)
加载原始图并拷入共享内存
raw_img = np.array(Image.open('input.jpg').convert('L').resize(shape)) np.copyto(img_view, raw_img) # 必须用 copyto,不能直接赋值
p = Process(target=worker, args=(shm.name, shape)) p.start() p.join()
shm.close() shm.unlink() # 关键:不 unlink 会导致下次运行报 OSError: File exists
实际图像流水线中,共享内存只适合中间数据,别碰输入/输出
图像预处理的 I/O 阶段(读文件、写文件)天然不适合共享——磁盘带宽和文件锁会成为瓶颈。共享内存真正起效的位置是「CPU密集型中间态」:
- 输入阶段:每个进程各自
Image.open()或cv2.imread(),独立加载(避免竞争) - 共享阶段:把加载后的
np.ndarray拷入共享内存,供多个进程并行做滤波、直方图均衡、二值化等 - 输出阶段:各进程从共享内存读出结果,再各自保存(
cv2.imwrite或np.save),不共享磁盘路径
如果强行让所有进程共用一个输入文件句柄或输出目录,大概率触发 OSError: [Errno 11] Resource temporarily unavailable 或文件损坏。
最易被忽略的一点:共享内存本身不提供同步机制。如果你有多个进程同时读写同一片区域(比如做图像分块处理),必须显式加 multiprocessing.Lock,否则像素值会随机覆盖——这种 bug 很难复现,但线上跑几万张图后才会暴露。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











