Python multiprocessing锁作为全局/局部变量实例化时表现差异咨询
问题原因分析
这个差异的核心是Python多进程启动规则和multiprocessing.Lock的跨进程传递逻辑共同导致的:
前置背景:Python多进程启动模式
Python的multiprocessing有三种进程启动模式,资源继承逻辑差异极大:
fork:仅Unix平台默认,直接复制父进程全部内存空间,所有fork前创建的对象都会被子进程继承spawn:Windows、macOS平台默认,子进程启动全新Python解释器,仅继承显式序列化传递的资源forkserver:轻量化的fork模式,使用场景较少
注意:Python对象的id()是进程虚拟地址空间内的本地标识,不同进程中指向同一个内核资源的Python对象id必然不同,不能作为判断资源是否共享的依据。
方案1正常工作的逻辑
你将锁定义为file2模块的全局变量时,锁的创建时机早于子进程的启动时机:
- 若使用
fork模式:子进程fork时会直接继承父进程创建的锁对应的内核句柄,所有进程操作的是同一个内核锁,互斥逻辑正常。 - 若使用
spawn模式:锁作为Process的args参数传递给子进程时,multiprocessing的同步原语会序列化内核锁的全局句柄,子进程反序列化后依然指向同一个内核锁,互斥逻辑正常。
方案2失效的逻辑
你将锁定义为run方法内的局部变量时,触发了Python 3.7版本的已知问题或代码逻辑问题:
- 版本序列化bug
Python 3.7.3存在局部作用域同步原语的序列化缺陷:在函数局部创建的Lock实例作为参数传递给子进程时,序列化逻辑会错误生成全新的内核锁实例,而非复用当前锁的句柄,导致每个子进程拿到的都是独立的锁,自然无法实现互斥。全局作用域的锁不会触发该bug。 - 代码逻辑错误
从你贴出的代码片段来看,multiprocessing.Process实例化的括号未闭合,如果实际运行的代码中不小心将锁的创建放到了for循环内部,每次循环都会生成新锁,每个子进程拿到的都是独立的锁,完全符合你给出的方案2运行结果。
修复方案
如果需要在局部作用域创建锁,可选择以下两种方案:
- 升级Python到3.8及以上版本,该序列化bug已被官方修复
- 使用
multiprocessing.Manager创建托管锁,托管锁的跨进程传递逻辑不受版本影响,兼容性更好:
def run(self): mgr = multiprocessing.Manager() package_lock = mgr.Lock() for i in range(3): self.proc.append( multiprocessing.Process( target=workingProcess, name=f"Process {i}", args=(i, package_lock,) ) ) self.proc[-1].start()
内容的提问来源于stack exchange,提问作者tyleax
相关产品推荐
相关产品推荐

