无法使用multiprocessing manager.dict()共享对象实例
我在用 multiprocessing
共享一个对象实例的字典时遇到了问题。我想用一个由 manager
共享的 dict
,但是当我试图用对象实例作为键时,它被复制了。
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()
对象中并不会发生这种情况。
另一种方法
在 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)
2 个回答
根据关于管理器的文档所说:
如果你在字典或列表的代理中修改了可变值或项目,这些修改不会通过管理器传播,因为代理无法知道它的值或项目何时被修改。要修改这样的项目,你需要把修改后的对象重新赋值给容器代理。
虽然multiprocessing
让多个进程之间的通信变得简单,但它仍然无法做操作系统不允许的事情(比如访问另一个进程的任意内存)。实际上,Manager
是对对象的副本进行操作,这些副本在需要时会被序列化。
我明白解决这个问题的方法是使用对象ID作为键。
请注意,你无法在其他进程中获取那些对象实例。正确的方法是在你修改对象时重新赋值。
当你创建一个 multiprocessing.Manager
的时候,会启动一个单独的服务器进程,这个进程负责管理所有由 Manager
创建的对象。所以,如果你想把你的 Dog
实例存储到 Manager
的 dict
中,这个实例需要被“打包”(也就是序列化)并发送到 Manager
进程。这样做的结果是,在 Manager
进程中会创建一个全新的 Dog
实例,因此它的ID和你在父进程中的 Dog
实例的ID是不同的。除了在 Manager
中也创建 Dog
实例作为 Proxy
实例外,没有其他办法可以避免这个问题:
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
实例的速度变慢,因为现在需要通过进程间通信(IPC)来访问 Manager
进程。