python SharedMemory

张开发
2026/4/5 16:31:25 15 分钟阅读

分享文章

python SharedMemory
# 聊聊Python里的SharedMemory最近在项目里用到了Python的multiprocessing.shared_memory模块感觉这东西挺有意思的。平时大家写多进程程序进程间通信是个绕不开的话题而共享内存算是其中比较直接的一种方式。他是什么简单来说SharedMemory就是一块内存区域多个Python进程都能访问这块内存。想象一下你有个白板放在办公室中间所有人都能在上面写字、擦除、修改内容。这个白板就是共享内存每个员工就是一个进程。不过要注意的是这个“白板”不是智能白板它不会自动同步内容。如果两个人同时往同一个位置写字最后写的内容会覆盖之前的内容。这既是共享内存的优势速度快也是需要小心的地方。在Python 3.8之前要实现类似的功能得用multiprocessing.Array或者multiprocessing.Value但那些用起来总感觉有点绕。SharedMemory出现后事情变得直接多了。他能做什么最典型的场景就是需要多个进程处理同一批数据而且数据量比较大。比如说你有个几GB的图像数据需要处理如果用队列或者管道在进程间传递光是复制数据就要花不少时间。用共享内存的话数据只存在一份各个进程直接去读去写省去了复制开销。另一个场景是进程间需要频繁交换数据。比如实时数据处理系统一个进程负责采集数据其他进程负责分析、存储、转发。如果每个数据包都通过IPC机制传递延迟可能会比较高。用共享内存做中间缓冲区效率会好很多。还有些情况是进程间需要共享状态。虽然Python有multiprocessing.Manager可以创建共享对象但那些对象通常是通过代理访问的性能不如直接操作内存。对于需要高性能状态共享的场景共享内存是个不错的选择。怎么使用用起来其实挺简单的。先创建一个共享内存块给它起个名字指定大小。其他进程通过这个名字就能找到这块内存然后像操作普通字节数组一样操作它。frommultiprocessingimportshared_memoryimportnumpyasnp# 创建共享内存shmshared_memory.SharedMemory(createTrue,size1024)print(f共享内存名字:{shm.name})# 写入数据buffershm.bufbuffer[0:10]bHello World# 在另一个进程中或者同一个进程的另一处existing_shmshared_memory.SharedMemory(nameshm.name)print(existing_shm.buf[0:10].tobytes())# 输出: bHello World# 用完后记得关闭和清理existing_shm.close()shm.close()shm.unlink()实际项目中经常会把共享内存和numpy数组结合起来用。因为很多数据处理都是数值计算用numpy方便很多。# 创建共享内存并包装成numpy数组shmshared_memory.SharedMemory(createTrue,size1000)arraynp.ndarray((250,),dtypenp.float32,buffershm.buf)# 填充数据array[:]np.random.randn(250)# 其他进程可以这样访问existing_shmshared_memory.SharedMemory(nameshm.name)same_arraynp.ndarray((250,),dtypenp.float32,bufferexisting_shm.buf)这里有个细节要注意所有进程对数据类型的理解必须一致。如果创建时用的是float32其他进程也必须用float32来解读否则数据就乱套了。最佳实践用了段时间后总结出几个经验。首先是内存管理。共享内存创建后不会自动释放必须显式调用unlink()。这有点像文件操作创建了文件得记得删除。比较好的做法是用try-finally或者上下文管理器确保资源被正确清理。classSharedMemoryContext:def__init__(self,nameNone,size0):self.shmshared_memory.SharedMemory(createTrue,namename,sizesize)def__enter__(self):returnself.shmdef__exit__(self,exc_type,exc_val,exc_tb):self.shm.close()self.shm.unlink()其次是并发控制。共享内存本身不提供锁机制如果多个进程同时写同一块内存数据可能会损坏。需要根据实际情况加锁可以用multiprocessing.Lock但要注意锁的粒度。锁得太细性能受影响锁得太粗并发效果不好。数据对齐也是个需要注意的点。特别是和C扩展库交互时如果共享内存要传递给C函数可能需要考虑内存对齐问题。Python层面不太明显但跨语言时就会暴露出来。还有个实践是给共享内存起有意义的名字。系统重启后共享内存名字可能被重用如果程序崩溃后重启可能会连接到错误的内存块。可以在名字里加入进程ID、时间戳等信息减少冲突概率。和同类技术对比Python里进程间通信的方式不少各有各的适用场景。multiprocessing.Queue是最常用的用起来简单线程安全但数据需要序列化反序列化对于大数据量性能开销大。适合传递消息、命令这种小数据。multiprocessing.Pipe和队列类似但更轻量不过同样有序列化开销。multiprocessing.Manager可以创建共享对象比如列表、字典用起来像本地对象但所有操作都通过代理性能一般。适合共享配置、状态这种不需要频繁操作的数据。mmap模块也能实现共享内存而且更底层跨平台兼容性好。但mmap通常需要文件系统支持而SharedMemory可以在纯内存中操作。如果不需要持久化SharedMemory更简洁。和这些技术相比SharedMemory的优势很明显速度快没有序列化开销内存使用效率高。但缺点也很明显需要自己处理并发数据类型要手动管理出错时调试比较困难。选择哪种技术得看具体需求。如果数据量大、对性能要求高、进程生命周期相同共享内存是个好选择。如果只是传递简单消息或者需要跨机器通信那还是用队列、管道更合适。最后说点个人感受。共享内存这种技术用好了能大幅提升性能用不好就是调试的噩梦。它给了开发者很大的自由度也带来了相应的责任。在决定使用之前最好先评估下是否真的需要有没有更简单的方案。很多时候简单的方案虽然性能差一点但维护成本低长期来看更划算。技术选型就是这样没有绝对的好坏只有适合不适合。共享内存是个好工具但要知道什么时候用它怎么用好它。

更多文章