You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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调用,是基于通用场景的最佳实践,没有覆盖你这个特殊使用场景,主要原因有两个:

  1. 很多教程的示例基于POSIX平台(Linux/macOS),POSIX的命名共享内存生命周期和Windows不同,就算所有进程都关闭了句柄,只要你不主动调用shm_unlink删除,共享内存段会一直保留,不存在关闭句柄就自动销毁的问题
  2. 多数教程的mmap使用示例是单任务一次性使用场景:打开mmap、做完读写操作后就不需要再复用这段内存,自动关闭是合理的,不会涉及到循环复用同一段命名共享内存的需求,自然不会提到这个Windows平台特有的坑

修复方案

如果需要在循环中复用同一段共享内存,可以采用两种方案:

  1. 将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)
  1. 如果确实需要每次循环都释放句柄,确保有其他进程持有该共享内存的句柄,避免系统自动销毁内存段。

内容的提问来源于stack exchange,提问作者oraant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.13 02:15:02