我们可以通过如下方式创建一个新的共享内存块,并实例化一个关联到这个新的共享内存块的 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()
在下例中,step1
、step2
、……、step8
会被依次运行。
step1
:在进程process-1
中,构造名为shm_name
的共享内存块,并实例化 SharedMemory 对象shm1
,此时只有shm1
关联到共享内存块,共享内存块的引用计数为 1 1 1。step2
:在进程process-2
中,根据名称shm_name
的共享内存块实例化 SharedMemory 对象shm2
,此时shm1
和shm2
都关联到共享内存块,共享内存块的引用计数为 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
,此时shm2
和shm3
都关联到共享内存块,共享内存块的引用计数为 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'