Python多进程中shared_memory、Process与Lock的行为困惑
Python多进程共享内存与锁的问题排查
问题背景
我在Windows系统上用Python 3.9学习multiprocessing库,尝试通过shared_memory模块在主进程和子进程间共享numpy数组。我写了一个测试脚本:
- 定义了包含共享内存
shm和Lock的类Foo - 实例化
Foo后启动子进程,子进程通过Lock修改并打印共享数组 - 主进程同样通过
Lock调用方法打印数组
实际输出
[0 0 0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0]
预期输出
[0 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0] [1 0 0 0 0 0 0 0 0 0]
奇怪的是,给主进程加time.sleep(2)就能得到预期结果。我原本以为Lock会让主进程的foo.show()等待子进程释放锁,不明白为什么会出现这种情况。另外,把整个类实例传给子进程是不是不良实践?会不会因为pickle序列化导致共享内存被误用?
问题解答
1. 为什么Lock没按预期同步,加sleep就正常?
这是Windows平台spawn启动方式的特性导致的:
- Windows下子进程是通过重新导入模块、重新初始化代码来启动的,这个过程需要一定时间
- 主进程在调用
p.start()后立刻执行foo.show(),此时子进程还没完成启动,更没来得及获取锁修改数组 - 主进程先抢占了Lock,打印初始数组后释放锁,之后子进程才启动并获取锁修改数组,所以会出现两次初始值的输出
- 加
time.sleep(2)相当于给子进程留足了启动和修改的时间,主进程调用show()时,子进程已经完成修改并释放了锁,自然能看到修改后的值
Lock本身是正常工作的,它保证了同一时间只有一个进程操作数组,但它无法控制主进程和子进程的执行顺序——主进程的show()操作只是比子进程的修改操作先执行了而已。
2. 传递整个类实例给子进程是不是不良实践?
是的,这属于不推荐的实践,原因如下:
- Windows的
spawn模式下,传递类实例会触发pickle序列化,虽然SharedMemory和Lock对象可以被序列化,但序列化的只是它们的标识信息(比如共享内存名称、锁的句柄),子进程会重新连接这些资源,而非直接复用主进程的实例 - 这种方式容易引入意外行为:比如类中如果还有其他状态变量,子进程序列化得到的是主进程实例的快照,后续主进程修改这些变量不会同步到子进程,反之亦然
- 更规范的做法是拆分资源传递:单独传递共享内存的名称、numpy数组的形状和dtype,以及
Lock对象,在子进程中重新构建与共享内存绑定的numpy数组,而不是传递整个类实例。这样能更清晰地控制多进程间的资源共享逻辑,避免序列化带来的潜在问题。
内容的提问来源于stack exchange,提问作者abc
相关产品推荐
相关产品推荐

