Python 语法特性|Windows环境中SharedMemory共享内存的回收机制

本文详细阐述了如何在Python中创建和管理共享内存,包括创建新内存块、通过已有名称实例化、引用计数与自动回收机制。通过示例展示了引用计数在共享内存回收中的作用,以及如何避免因生命周期管理不当导致的错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们可以通过如下方式创建一个新的共享内存块,并实例化一个关联到这个新的共享内存块的 SharedMemory 对象:

shm = shared_memory.SharedMemory(name="shm_name", create=True, size=40)

我们还可以通过如下方式,实例化一个关联到一个已经存在的共享内存块的 SharedMemory 对象:

shared_memory.SharedMemory(name="shm_name")

除了在 Python3 官方文档:multiprocessing.shared_memory --- 可从进程直接访问的共享内存 中描述的通过 close() 方法和 unlink() 方法回收共享内存的方法以外,Python 还配置一套自动的共享内存块回收机制。

自动回收机制可以概括为:

当所有进程中,关联到这个共享内存块的 SharedMemory 对象的生命周期都已经结束后,那么该共享内存块也将被回收。

需要注意的是,共享内存块的生命周期并不依赖于某个进程,只要有任意一个进程(不需要必须是构造共享内存块的进程)中有关联到这个共享内存块的 SharedMemory 对象,该共享内存都不会被回收。

我们也可以这样不严谨地理解:共享内存块作为一个对象,也适用于 Python 基于引用计数的回收机制。共享内存块只能通过关联的 SharedMemory 对象引用,因此如果所有的关联到这个共享内存块的 SharedMemory 对象的生命周期都已经结束,那么这个共享内存块的引用计数为 0 0 0,于是这个共享内存块也将被回收。

当共享内存块被回收后,我们将无法通过 shared_memory.SharedMemory(name="shm_name") 来实例化关联到这个共享内存块的 SharedMemory 对象。

我们通过下面的用例来理解。

import time
from multiprocessing import Lock
from multiprocessing import Process
from multiprocessing import shared_memory


def process1(lock1, lock2):
    """构造共享内存的进程"""
    lock1.acquire()
    print("step1...")
    shm1 = shared_memory.SharedMemory(name="shm_name", create=True, size=4000)
    print("构造新的共享内存,实例化SharedMemory对象{}".format(shm1))
    lock2.release()

    lock1.acquire()
    print("step3...")
    shm1 = None
    lock2.release()

    lock1.acquire()
    print("step5...")
    shm3 = shared_memory.SharedMemory(name="shm_name")
    print("构造新的共享内存,实例化SharedMemory对象{}".format(shm3))
    lock2.release()

    lock1.acquire()
    print("step7...")
    lock2.release()


def process2(lock1, lock2):
    """使用修改共享内存的进程"""
    lock2.acquire()
    print("step2...")
    shm2 = shared_memory.SharedMemory(name="shm_name")
    print("使用现有共享内存,实例化SharedMemory对象{}".format(shm2))
    lock1.release()

    lock2.acquire()
    print("step4...")
    time.sleep(1)  # 添加延迟保证需要被销毁的共享内存块已被回收
    lock1.release()

    lock2.acquire()
    print("step6...")
    shm2 = None
    lock1.release()

    lock2.acquire()
    print("step8...")
    time.sleep(1)  # 添加延迟保证需要被销毁的共享内存块已被回收
    shm4 = shared_memory.SharedMemory(name="shm_name")
    print("使用现有共享内存,实例化SharedMemory对象{}".format(shm4))


if __name__ == "__main__":
    main_lock1, main_lock2 = Lock(), Lock()
    main_lock2.acquire()
    p1 = Process(target=process1, args=(main_lock1, main_lock2,))
    p2 = Process(target=process2, args=(main_lock1, main_lock2,))
    p1.start()
    p2.start()

在下例中,step1step2、……、step8 会被依次运行。

  • step1:在进程 process-1 中,构造名为 shm_name 的共享内存块,并实例化 SharedMemory 对象 shm1,此时只有 shm1 关联到共享内存块,共享内存块的引用计数为 1 1 1
  • step2:在进程 process-2 中,根据名称 shm_name 的共享内存块实例化 SharedMemory 对象 shm2,此时 shm1shm2 都关联到共享内存块,共享内存块的引用计数为 2 2 2
  • step3:在进程 process-1 中,将 shm1 置为空,别名 shm1 之前的对应的对象的生命周期结束,此时只有 shm2 关联到共享内存块,共享内存块的引用计数为 1 1 1
  • step4:在进程 process-2 中延时 1 1 1 秒,以保证需要被销毁的共享内存块已被销毁,但此时共享内存块不会被回收。
  • step5:在进程 process-1 中,根据名称 shm_name 的共享内存块实例化 SharedMemory 对象 shm3,此时 shm2shm3 都关联到共享内存块,共享内存块的引用计数为 2 2 2
  • step6:在进程 process-2 中,将 shm2 置为空,别名 shm2 之前的对应的对象的生命周期结束,此时 shm3 关联到共享内存块,共享内存块的引用计数为 1 1 1
  • step7:进程 process-1 运行完成,对象 shm3 的生命周期结束,此时共享内存块的引用计数为 0 0 0,共享内存块被回收。
  • step8:在进程 process-2 中,根据名称 shm_name 的共享内存块实例化 SharedMemory 对象 shm4,但是因为名称为 shm_name 的共享内存块已被回收,所以无法找到对应的共享内存块而报错。

上例运行输出如下:

step1...
构造新的共享内存,实例化SharedMemory对象SharedMemory('shm_name', size=4000)
step2...
使用现有共享内存,实例化SharedMemory对象SharedMemory('shm_name', size=4096)
step3...
step4...
step5...
构造新的共享内存,实例化SharedMemory对象SharedMemory('shm_name', size=4096)
step6...
step7...
step8...
Process Process-2:
Traceback (most recent call last):
  File "C:\Program Files\Python39\lib\multiprocessing\process.py", line 315, in _bootstrap
    self.run()
  File "C:\Program Files\Python39\lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
  File "E:\python-technique\PythonLibrary\A01_SharedMemory\example_1.py", line 56, in process2
    shm4 = shared_memory.SharedMemory(name="shm_name")
  File "C:\Program Files\Python39\lib\multiprocessing\shared_memory.py", line 161, in __init__
    h_map = _winapi.OpenFileMapping(
FileNotFoundError: [WinError 2] 系统找不到指定的文件。: 'shm_name'
Windows API中,`NOCACHE`标志通常用于`CreateFileMapping`函数,它用于创建映射文件到进程地址空间的操作。这个标志表示系统不应该将映射区域的内容加载到缓存中,而是直接从磁盘读取数据。这可以提高数据一致性,尤其是在处理实时更新的数据时。 在C语言中,使用`NOCACHE`的示例如下: ```c #include <windows.h> #include <stdio.h> #define FILE_NAME L"YourSharedMemoryFileName" #define MAP_SIZE (1024 * 1024) // 1MB // 创建映射文件 LPVOID CreateNoCacheMap(HANDLE hFile) { DWORD flProtect = PAGE_READWRITE; HANDLE hMap = CreateFileMapping(hFile, NULL, flProtect, 0, MAP_SIZE, L"NOCACHE"); if (hMap == NULL) { printf("Failed to create mapping with NOCACHE flag.\n"); return NULL; } LPVOID mappedView =MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, 0, 0, MAP_SIZE); if (mappedView == NULL) { CloseHandle(hMap); printf("Failed to map view of file with NOCACHE flag.\n"); return NULL; } // 现在你可以在这个映射视图上操作数据... // 示例结束后记得UnmapViewOfFile和CloseHandle return mappedView; } int main() { HANDLE hFile = CreateFile(L"\\\\.\\GLOBAL??\\CON", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Error opening file handle.\n"); return 1; } LPVOID sharedMemory = CreateNoCacheMap(hFile); if (sharedMemory != NULL) { // 在这里操作共享内存... UnmapViewOfFile(sharedMemory); CloseHandle(hFile); } else { CloseHandle(hFile); } return 0; } ``` 在这个示例中,我们首先打开一个文件句柄,然后将其与指定大小的内存区域关联起来,并使用`NOCACHE`标志告诉系统。记住,在完成操作后,需要调用`UnmapViewOfFile`释放映射视图并关闭文件句柄。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值