1

我在与multiprocessing. 我正在尝试使用 adict共享的 a 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()我注意到普通的非对象不会发生这种情况。

替代方法

在 的文档中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) 
4

2 回答 2

3

当你创建multiprocessing.Manager一个Manager. 因此,为了将您的Dog实例存储在 中Manager dict,需要对实例进行腌制并将其发送到Manager进程。当然,这会导致在进程Dog中创建一个完全独立的实例,因此它的 ID 与父进程Manager中的实例 ID 不匹配。除了将实例创建为 中的实例之外Dog,没有办法避免这种情况:DogProxyManager

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 调用。

于 2014-10-14T19:04:22.077 回答
2

正如关于管理器的文档所述:

对 dict 和 list 代理中的可变值或项的修改不会通过管理器传播,因为代理无法知道其值或项何时被修改。要修改这样的项目,您可以将修改后的对象重新分配给容器代理

虽然multiprocessing使多个进程之间的通信变得容易,但它仍然不能做操作系统不允许的事情(访问另一个进程的任意内存)。在实践中,Manager我们在对象的副本上工作,这些副本在需要时被序列化。

我了解解决此问题的方法是使用对象 ID 作为键

请注意,您将无法在其他进程中获取这些对象实例。“正确”的方法是在更改对象时重新分配对象。

于 2014-10-14T18:41:16.280 回答