此代码未按您预期的方式工作的原因是多处理不与子进程共享其状态。这意味着您启动的每个流程,p1
, p2
and p3
, 获取类对象的副本Shop
。它不是同一个对象。有two解决此问题的方法是共享实例属性stock
与进程共享,或共享整个对象本身。如果商店对象保存需要在进程之间共享的其他数据,则第二种方法可能更适合您的较大用例。
Method 1:
只分享价值stock
实例变量,您可以使用多处理.值。使用它创建共享整数并访问它们的值的方法如下:
shared_int = multiprocessing.Value('i', 5)
print(f'Value is {shared_int.value}') # 5
根据您的用例,代码将变为:
import multiprocessing
class Shop:
def __init__(self, stock=5):
self.stock = multiprocessing.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 = multiprocessing.Lock()
obj = Shop()
p1 = multiprocessing.Process(target=obj.get_item, args=(l, 1))
p2 = multiprocessing.Process(target=obj.get_item, args=(l, 1))
p3 = multiprocessing.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)
Output
4 = remaining
3 = remaining
2 = remaining
Final: 2
Method 2
共享整个复杂对象是一个更复杂的过程。我最近有过answered关于共享复杂对象(例如本例中 Shop 类的对象)的详细类似问题,其中还涵盖了下面提供的代码背后的推理。我建议您阅读它,因为它更详细地解释了底部提供的代码背后的逻辑。此用例的唯一主要区别是您需要使用多进程,多处理的一个分支,而不是多处理。该库的工作方式与内置多处理相同,除了它提供了我们需要的更好的酸洗支持。
基本上,你会想要使用多处理管理器共享状态,以及访问状态的合适代理。这ObjProxy
下面的代码中提供了这样一种代理,它共享命名空间以及实例方法(除了受保护/私有属性之外)。一旦你有了这些,你只需要创建类的对象Shop
使用管理器和代理。这是使用新添加的create
类的方法Shop
。这是一个类构造函数和所有对象Shop
应仅使用此方法创建,而不是直接调用构造函数。完整代码:
import multiprocess
from multiprocess import Manager, Process
from multiprocess.managers import NamespaceProxy, BaseManager
import types
class ObjProxy(NamespaceProxy):
"""Returns a proxy instance for any user defined data-type. The proxy instance will have the namespace and
functions of the data-type (except private/protected callables/attributes). Furthermore, the proxy will be
pickable and can its state can be shared among different processes. """
def __getattr__(self, name):
result = super().__getattr__(name)
if isinstance(result, types.MethodType):
def wrapper(*args, **kwargs):
return self._callmethod(name, args, kwargs)
return wrapper
return result
class Shop:
def __init__(self, stock=5):
self.stock = stock
@classmethod
def create(cls, *args, **kwargs):
# Register class
class_str = cls.__name__
BaseManager.register(class_str, cls, ObjProxy, exposed=tuple(dir(cls)))
# Start a manager process
manager = BaseManager()
manager.start()
# Create and return this proxy instance. Using this proxy allows sharing of state between processes.
inst = eval("manager.{}(*args, **kwargs)".format(class_str))
return inst
def get_item(self, l, x):
with l:
if self.stock >= x:
self.stock -= x
print(f"{self.stock} = remaining")
def k(self, l, n):
pass
if __name__ == "__main__":
manager = Manager()
l = manager.Lock()
obj = Shop.create()
p1 = Process(target=obj.get_item, args=(l, 1, ))
p2 = Process(target=obj.get_item, args=(l, 1, ))
p3 = Process(target=obj.get_item, args=(l, 1, ))
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
print("Final: ", obj.stock)
Output
4 = remaining
3 = remaining
2 = remaining
Final: 2
Note:这两行的解释:
manager = Manager()
l = manager.Lock()
在您的示例中概述了我们之前不需要为锁创建管理器(以及随后的代理)的原因here。之所以在不创建代理的情况下使用上面的代码不起作用,是因为我们不再在主进程中创建进程,并且当前进程内存空间中不存在锁(自从为我们的复杂对象创建管理器以来)共享其状态产生了自己的服务器进程)