多进程中传递Lock作为参数为何可行?副本锁生效原因探究
你观察到每个进程里Lock对象的内存地址不一样,以为是独立副本,这很正常——但这只是用户态的表象,真正起作用的锁根本不在这个用户态对象里,而是藏在操作系统内核中。下面给你拆解关键逻辑:
Lock的本质是内核级同步资源
Python的multiprocessing.Lock只是个“包装壳”,真正的锁是操作系统内核创建的同步原语(比如Linux的互斥量mutex、Windows的临界区)。当你在主进程创建Lock时,内核会生成一个唯一的、跨进程可见的同步对象,这个对象才是控制进程互斥的核心。传递Lock时,传的是内核资源的“引用”而非对象本身
当你把Lock作为参数传给子进程时,多进程机制(不管是fork还是spawn启动方式)会把这个Lock对应的内核资源标识符传递给子进程。子进程拿到这个标识符后,会新建一个Lock对象来包装它——所以你看到的是不同的用户态对象(地址不同),但它们背后绑定的是同一个内核锁资源。虚拟地址空间的“错觉”
每个进程都有独立的虚拟地址空间,同一个内核资源在不同进程里的用户态包装对象,虚拟地址肯定不一样,但这完全不影响内核锁的统一性。就像你和同事各拿一张门禁卡(用户态Lock对象),卡的物理形态不同,但都能打开同一扇公司大门(内核锁)——不管谁刷了卡锁门,其他人都进不去。反例验证:如果每个进程自己新建Lock会怎样?
要是你不在主进程创建Lock再传递,而是让每个子进程自己新建Lock(),那每个Lock都会对应一个独立的内核同步对象,这时acquire()就完全起不到互斥作用了——因为它们锁的根本不是同一个“门”。
总结一下:你看到的用户态Lock对象只是内核锁的“代言人”,只要这些代言人绑定的是同一个内核资源,不管它们在不同进程里的地址是什么样,同步逻辑都能正常工作。
内容的提问来源于stack exchange,提问作者Kostas Oreopoulos

