Python类内多进程实例变量ID不一致问题及原理咨询
问题解答
1. 首次与后续迭代实例变量ID差异的原因及进程启动方式的影响
在Ubuntu系统中,Python multiprocessing 默认使用 fork 启动模式,这是导致你看到现象的核心原因:
- 首次迭代ID唯一:第一次创建
Pool时,父进程刚完成CallingClass实例化,self.parentStuffList是新初始化的对象。fork会复制父进程的整个地址空间(采用写时复制COW机制),但每个子进程拥有独立的虚拟地址空间。Python的id()返回的是对象在当前进程虚拟地址空间的内存地址,首次fork时,各个子进程的虚拟地址分配存在差异,因此每个子进程中self.parentStuffList的ID是唯一的。 - 后续迭代ID一致:后续循环中,父进程的
self.parentStuffList已存在且未被修改。子进程fork时,COW机制会让子进程共享该对象的物理内存,且由于没有修改操作触发内存拷贝,各个子进程的虚拟地址空间中,该对象的虚拟地址被分配为相同值,最终打印的ID一致。
若切换到spawn启动模式(通过multiprocessing.set_start_method('spawn')设置),行为会完全不同:
spawn会启动全新的Python解释器,重新加载代码并初始化对象。每个子进程都会重新创建CallingClass及父类ParentClass的实例,self.parentStuffList是全新的对象,其ID会与父进程不同;同时,不同子进程中重新创建的同一结构对象,可能在各自地址空间中分配到相同虚拟地址,导致子进程间的ID一致。
2. 多进程中父类继承数据的实例数量
不管使用fork还是spawn模式,每个子进程都会拥有父类继承数据的独立副本,即N个进程会生成N份实例:
- fork模式:子进程复制父进程的完整内存空间,包括父类的实例数据。初始阶段通过COW共享物理内存,但只要子进程修改该数据,就会触发内存拷贝,生成完全独立的副本。
- spawn模式:子进程会重新执行代码流程,从头初始化
CallingClass及其父类ParentClass,因此会创建全新的父类实例,数据完全独立于父进程和其他子进程。
内容的提问来源于stack exchange,提问作者Anthony Nash
相关产品推荐
相关产品推荐

