Python multiprocessing Lock未阻塞对象访问问题及解决方法
问题场景
如下代码模拟了商店场景:商店初始库存为5件商品,共有3位顾客,每位顾客需购买1件商品。
import multiprocessing as mp class Shop: def __init__(self, stock=5): self.stock = stock def get_item(self, l, x): l.acquire() if self.stock >= x: self.stock -= x print(f"{self.stock} = remaining") l.release() if __name__ == "__main__": l = mp.Lock() obj = Shop() p1 = mp.Process(target=obj.get_item, args=(l, 1)) p2 = mp.Process(target=obj.get_item, args=(l, 1)) p3 = mp.Process(target=obj.get_item, args=(l, 1)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print("Final: ", obj.stock)
实际运行输出
4 = remaining 4 = remaining 4 = remaining Final: 5
预期输出
代码中已经使用了Lock做同步,预期得到如下输出:
4 = remaining 3 = remaining 2 = remaining Final: 2
核心问题
如何仅使用Lock(不使用Pipe/Queue等进程通信机制)实现上述预期输出?
问题原因
原有代码失效的核心原因是多进程默认内存隔离:
multiprocessing创建的子进程拥有独立的地址空间,传入子进程的Shop实例会被拷贝一份,实例属性stock是每个进程私有的,子进程修改自己的副本不会影响其他子进程,也不会同步回主进程。- 代码中的
Lock确实是跨进程生效的,但它保护的只是每个进程对自己私有stock的修改操作,完全没有解决跨进程状态共享的问题,因此才会出现三个进程都把自己的库存从5减到4、主进程库存始终为5的现象。
解决方案
Lock本身只是同步原语,不具备存储共享状态的能力,要实现跨进程状态同步,只需要把库存值放到所有进程都能访问的共享内存中,再配合现有Lock做原子操作保护即可——共享内存不属于题目禁止的Pipe/Queue类消息通信机制,完全符合要求。
修改后的可运行代码如下:
import multiprocessing as mp class Shop: def __init__(self, stock=5): # 用跨进程共享的内存块存储库存,'i'对应C语言int类型 self.stock = mp.Value('i', stock) def get_item(self, l, x): l.acquire() if self.stock.value >= x: self.stock.value -= x print(f"{self.stock.value} = remaining") l.release() if __name__ == "__main__": l = mp.Lock() obj = Shop() p1 = mp.Process(target=obj.get_item, args=(l, 1)) p2 = mp.Process(target=obj.get_item, args=(l, 1)) p3 = mp.Process(target=obj.get_item, args=(l, 1)) p1.start() p2.start() p3.start() p1.join() p2.join() p3.join() print("Final: ", obj.stock.value)
运行后即可得到预期输出:Lock保证同一时间只有一个进程能修改共享内存中的库存值,三个进程依次扣减库存,最终主进程读取到的共享内存库存值为2。
内容的提问来源于stack exchange,提问作者Lawhatre
相关产品推荐
相关产品推荐

