我在使用multiprocessing
共享对象实例字典时遇到问题。我正在尝试使用由dict
共享的manager
,但是当我尝试将对象实例用作键时,它会被复制。
import multiprocessing
class Dog():
def __init__(self, name = "joe"):
self.name = name
def bark(self):
print("woof")
mg = multiprocessing.Manager()
dt = mg.dict()
dt["a"] = 1
dt["b"] = 2
# As expected
print(dt.items()) # => [('a', 1), ('b', 2)]
dt = mg.dict()
lab = Dog("carl")
print(lab) # => <__main__.Dog instance at 0x7f8d6bb869e0>
dt[lab] = 1
# But then I don't get the ID I expect
print(dt.items()) # => [(<__main__.Dog instance at 0x7f8d6bb86908>, 1)]
我理解解决这个问题的方法是使用对象ID作为密钥,但为什么会发生这种情况?使用对象ID是我问题的最佳解决方案吗?我注意到普通的非manager
dict()
对象不会发生这种情况。
替代方法
In the documentation for Manager()
,我读到一些问题是通知服务器更改,所以我将代码更改为此,但我仍然遇到同样的问题,我的狗被复制,没有被引用。
import multiprocessing
class Dog():
def __init__(self, name = "joe"):
self.name = name
def bark(self):
print("woof")
mg = multiprocessing.Manager()
dt = dict()
lp = mg.list()
lp.append(dt)
print(lp)
dt["a"] = 1
dt["b"] = 2
lp[0] = dt
print(lp)
dt = dict()
lab = Dog("carl")
print(lab)
pup = Dog("steve")
print(pup)
dt[lab] = 1
dt[pup] = 2
lp[0] = dt
# Their ids change again
print(lp)
答案 0 :(得分:3)
当您创建multiprocessing.Manager
时,会生成一个单独的服务器进程,该进程负责托管Manager
创建的所有对象。因此,为了将Dog
实例存储在Manager
dict
中,需要对实例进行pickle并将其发送到Manager
进程。当然,这会导致在Dog
进程中创建一个完全独立的Manager
实例,因此其ID将与父进程中Dog
实例的ID不匹配。除了在Dog
中创建Proxy
实例作为Manager
实例之外,没有办法避免这种情况:
import multiprocessing
from multiprocessing.managers import SyncManager
def Manager():
m = SyncManager()
m.start()
return m
class Dog():
def __init__(self, name = "joe"):
self.name = name
def bark(self):
print("woof")
SyncManager.register("Dog", Dog)
mg = Manager()
dt = dict()
lp = mg.list()
lp.append(dt)
print(lp)
dt["a"] = 1
dt["b"] = 2
lp[0] = dt
print(lp)
dt = dict()
lab = mg.Dog("carl")
print(lab)
pup = mg.Dog("steve")
print(pup)
dt[lab] = 1
dt[pup] = 2
lp[0] = dt
# Their ids don't change
print(lp)
输出:
<__main__.Dog instance at 0x1780098>
<__main__.Dog instance at 0x177efc8>
[{<__main__.Dog instance at 0x1780098>: 1, <__main__.Dog instance at 0x177efc8>: 2}]
请记住,这会使父进程中Dog
个实例的所有访问速度变慢,因为它们现在需要对Manager
进程进行IPC调用。
答案 1 :(得分:2)
正如the documentation on managers所述:
对dict和列表代理中的可变值或项的修改将会 不能通过管理器传播,因为代理无法通过 知道何时修改其值或项目。要修改这样的项目, 您可以将修改后的对象重新分配给容器代理
虽然multiprocessing
使多个进程之间的通信变得容易,但它仍然无法执行操作系统不允许的操作(访问另一个进程的任意内存)。在实践中,Manager
处理对象的副本,这些副本在需要时被序列化。
我理解解决这个问题的方法是使用对象ID作为键
请注意,您将无法在其他进程中获取这些对象实例。 “正确”的方法就是在更改对象时重新分配对象。