使用contextlib.closing封装mmap时出现读取失败问题的原因是什么?
问题原因分析
核心问题根源
你遇到的这个问题是Windows平台命名共享内存特有的生命周期规则和contextlib.closing的自动清理逻辑冲突导致的:
contextlib.closing包裹mmap实例后,with代码块执行结束时会自动调用mmap对象的close()方法,释放当前持有的共享内存句柄- Windows系统的命名共享内存(也就是你用
tagname参数创建的共享内存),会在所有持有该内存段的句柄都被关闭后,自动销毁整个共享内存段 - 你第一个写法没有主动调用
close(),每次循环创建的mmap对象持有的句柄都没有释放,共享内存段一直保留,后续循环打开同名tagname时会直接复用已有的内存段,所以能读到之前写入的数据 - 改用
closing封装后,每次循环结束都会关闭当前的mmap句柄,单进程测试场景下没有其他进程持有该共享内存的句柄,系统就会直接销毁这段内存,下一次循环再调用mmap.mmap()时会创建一个全新的、初始全为0的共享内存段,所以每次读到的cnt都是0,反复触发写入逻辑
为什么教程没提到这个问题
大部分教程推荐用contextlib.closing封装mmap调用,是基于通用场景的最佳实践,没有覆盖你这个特殊使用场景,主要原因有两个:
- 很多教程的示例基于POSIX平台(Linux/macOS),POSIX的命名共享内存生命周期和Windows不同,就算所有进程都关闭了句柄,只要你不主动调用
shm_unlink删除,共享内存段会一直保留,不存在关闭句柄就自动销毁的问题 - 多数教程的mmap使用示例是单任务一次性使用场景:打开mmap、做完读写操作后就不需要再复用这段内存,自动关闭是合理的,不会涉及到循环复用同一段命名共享内存的需求,自然不会提到这个Windows平台特有的坑
修复方案
如果需要在循环中复用同一段共享内存,可以采用两种方案:
- 将mmap的初始化逻辑移到循环外部,用
with包裹整个循环逻辑,全程只持有一个句柄,循环结束后再自动关闭:
import mmap import contextlib from time import sleep with contextlib.closing(mmap.mmap(-1, 1024, access=mmap.ACCESS_WRITE, tagname='share_mmap')) as m: for i in range(10): sleep(1) m.seek(0) cnt = m.read_byte() if cnt == 0: print("Load data to memory") m.seek(0) m.write(b"FFFFFFFFFFFFFFFFFF") else: m.seek(0) info_str=m.read().translate(None, b'\x00').decode() print("The data is in memory: ", info_str)
- 如果确实需要每次循环都释放句柄,确保有其他进程持有该共享内存的句柄,避免系统自动销毁内存段。
内容的提问来源于stack exchange,提问作者oraant
相关产品推荐
相关产品推荐

