You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 09:50:42