我想弄清楚Lock是如何工作的。我在MacOS上运行这段代码,使用"spawn"作为启动新进程的默认方法。
from multiprocessing import Process, Lock, set_start_method
from time import sleep
def f(lock, i):
lock.acquire()
print(id(lock))
try:
print('hello world', i)
sleep(3)
finally:
lock.release()
if __name__ == '__main__':
# set_start_method("fork")
lock = Lock()
for num in range(3):
p = Process(target=f, args=(lock, num))
p.start()
p.join()
输出:
140580736370432
hello world 0
140251759281920
hello world 1
140398066042624
hello world 2
锁在我的代码中工作。但是,锁的id让我很困惑。由于id
是不同的,它们仍然是相同的一个锁还是有多个锁并且它们以某种方式秘密通信?是否id()
仍然保持在多处理中的位置,我引用"CPython实现细节:id是内存中对象的地址"?
如果我使用"叉"方法,set_start_method("fork")
,它打印出相同的id
,这对我来说完全有意义。
id
被实现为但不要求是给定对象的内存位置。当使用fork时,单独的进程不会得到它自己的内存空间,直到它修改了一些东西(写时复制),所以内存位置不会改变,因为它"是"。同样的物体。当使用spawn时,将创建一个全新的进程,并将__main__文件作为库导入到本地命名空间中,因此所有相同的函数,类和模块级变量都是可访问的(没有来自if __name__ == "__main__":
的任何修改)。然后python在进程之间创建一个连接(管道),它可以在其中发送要调用的函数和调用它的参数。通过这个管道的所有东西都必须是pickle
'd,然后是unpickle
'd。锁在unpickling时通过请求操作系统使用特定名称(该名称在创建锁时在父进程中创建,然后使用pickle发送此名称)来重新创建。这就是两个锁同步的方式,因为它是由操作系统控制的对象支持的。然后,Python将此锁与其他一些数据(PyObject)一起存储在新进程的内存中。现在调用id
将得到这个结构体的位置,这个位置是不同的,因为它是由不同的进程在不同的内存块中创建的。
这里有一个快速的例子来说服你一个"衍生"。锁仍然同步:
from multiprocessing import Process, Lock, set_start_method
def foo(lock):
with lock:
print(f'child process lock id: {id(lock)}')
if __name__ == "__main__":
set_start_method("spawn")
lock = Lock()
print(f'parent process lock id: {id(lock)}')
lock.acquire() #lock the lock so child has to wait
p = Process(target=foo, args=(lock,))
p.start()
input('press enter to unlock the lock')
lock.release()
p.join()
不同的"id' "是不同的PyObject位置,但与底层互斥锁关系不大。我不知道有一种直接的方法来检查操作系统管理的底层锁。