Python 如何使用multiprocessing.Manager()?

Python 如何使用multiprocessing.Manager()?,python,multiprocessing,python-2.x,Python,Multiprocessing,Python 2.x,我关心python中的多处理.Manager()。以下是一个例子: import multiprocessing def f(ns): ns.x *=10 ns.y *= 10 if __name__ == '__main__': manager = multiprocessing.Manager() ns = manager.Namespace() ns.x = 1 ns.y = 2 print 'before', ns p

我关心python中的
多处理.Manager()
。以下是一个例子:

import multiprocessing

def f(ns):
    ns.x *=10
    ns.y *= 10

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    ns = manager.Namespace()
    ns.x = 1
    ns.y = 2

    print 'before', ns
    p = multiprocessing.Process(target=f, args=(ns,))
    p.start()
    p.join()
    print 'after', ns
输出为:

before Namespace(x=1, y=2)
after Namespace(x=10, y=20)
before Namespace(x=[], y=[])
after Namespace(x=[], y=[])
到目前为止,它的工作方式与我预期的一样,然后我对代码进行了如下修改:

import multiprocessing

def f(ns):
    ns.x.append(10)
    ns.y.append(10)

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    ns = manager.Namespace()
    ns.x = []
    ns.y = []

    print 'before', ns
    p = multiprocessing.Process(target=f, args=(ns,))
    p.start()
    p.join()
    print 'after', ns
现在输出为:

before Namespace(x=1, y=2)
after Namespace(x=10, y=20)
before Namespace(x=[], y=[])
after Namespace(x=[], y=[])

这使我困惑,为什么清单没有像我预期的那样改变。有人能帮我弄清楚发生了什么吗?

Manager代理对象无法传播对容器内(非托管)可变对象所做的更改。换句话说,如果您有一个
manager.list()
对象,对托管列表本身的任何更改都会传播到所有其他进程。但是,如果该列表中有一个普通的Python列表,则不会传播对内部列表的任何更改,因为管理器无法检测到更改

为了传播更改,您也必须对嵌套列表使用
manager.list()
对象(需要),或者您需要直接修改
manager.list()
对象(请参见注释)

例如,考虑下面的代码及其输出:

import multiprocessing
import time

def f(ns, ls, di):
    ns.x += 1
    ns.y[0] += 1
    ns_z = ns.z
    ns_z[0] += 1
    ns.z = ns_z

    ls[0] += 1
    ls[1][0] += 1 # unmanaged, not assigned back
    ls_2 = ls[2]  # unmanaged...
    ls_2[0] += 1
    ls[2] = ls_2  # ... but assigned back
    ls[3][0] += 1 # managed, direct manipulation

    di[0] += 1
    di[1][0] += 1 # unmanaged, not assigned back
    di_2 = di[2]  # unmanaged...
    di_2[0] += 1
    di[2] = di_2  # ... but assigned back
    di[3][0] += 1 # managed, direct manipulation

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    ns = manager.Namespace()
    ns.x = 1
    ns.y = [1]
    ns.z = [1]
    ls = manager.list([1, [1], [1], manager.list([1])])
    di = manager.dict({0: 1, 1: [1], 2: [1], 3: manager.list([1])})

    print('before', ns, ls, ls[2], di, di[2], sep='\n')
    p = multiprocessing.Process(target=f, args=(ns, ls, di))
    p.start()
    p.join()
    print('after', ns, ls, ls[2], di, di[2], sep='\n')
输出:

before
Namespace(x=1, y=[1], z=[1])
[1, [1], [1], <ListProxy object, typeid 'list' at 0x10b8c4630>]
[1]
{0: 1, 1: [1], 2: [1], 3: <ListProxy object, typeid 'list' at 0x10b8c4978>}
[1]
after
Namespace(x=2, y=[1], z=[2])
[2, [1], [2], <ListProxy object, typeid 'list' at 0x10b8c4630>]
[2]
{0: 2, 1: [1], 2: [2], 3: <ListProxy object, typeid 'list' at 0x10b8c4978>}
[2]
之前
命名空间(x=1,y=[1],z=[1])
[1, [1], [1], ]
[1]
{0: 1, 1: [1], 2: [1], 3: }
[1]
之后
名称空间(x=2,y=[1],z=[2])
[2, [1], [2], ]
[2]
{0: 2, 1: [1], 2: [2], 3: }
[2]

如您所见,当一个新值直接分配给托管容器时,它会发生变化;当它被分配到托管容器中的可变容器时,它不会改变;但是,如果可变容器随后被重新分配给托管容器,它将再次更改。使用嵌套的托管容器也可以工作,直接检测更改而不必重新分配给父容器。

ns
是NamespaceProxy实例。这些对象具有特殊的
\uuuuu getattr\uuuuuu
\uuuuu setattr\uuuuuuu
方法,允许在进程之间共享值。 为了在更改值时利用此机制,必须触发
\uuuu setattr\uuuu

ns.x.append(10)
导致调用
ns.\uuuu getattr\uuuu
以检索
ns.x
,但它不会导致调用
ns.\uuuu setattr\uuuuu

要解决此问题,必须使用
ns.x=…

def f(ns):   
    tmp = ns.x     # retrieve the shared value
    tmp.append(10)
    ns.x = tmp     # set the shared value

从3.6开始,对嵌套对象的更改会自动传播。我在Python3.6.4的管理器中遇到了在命名空间中使用嵌套字典的一些问题。在向前移动之前,请确保嵌套对象已正确更新。我的解决方案是显式定义每个要共享的对象作为管理器对象。@max:前提是这些嵌套对象也是代理对象。此答案中的代码嵌套常规的非代理列表。
ls
di
中的嵌套列表需要包装在
manager.list()
调用中。@MartijnPieters,感谢您的澄清!我看我根本不明白那个评论。@MartijnPieters同样,并不是所有代理都可以嵌套工作。虽然它适用于列表和dict,但不适用于队列: