共享列表的多处理

时间:2014-05-13 05:06:55

标签: python multiprocessing

我写了一个这样的程序:

from multiprocessing import Process, Manager

def worker(i):
    x[i].append(i)

if __name__ == '__main__':
    manager = Manager()
    x = manager.list()
    for i in range(5):
        x.append([])
    p = []
    for i in range(5):
        p.append(Process(target=worker, args=(i,)))
        p[i].start()

    for i in range(5):
        p[i].join()

    print x

我想在进程之间创建一个列表的共享列表,每个进程都会在其中修改一个列表。但是这个程序的结果是一个空列表列表:[[],[],[],[],[]]。

出了什么问题?

1 个答案:

答案 0 :(得分:10)

我认为这是因为管理者实施方式的怪癖。

如果您创建了两个Manager.list对象,然后将其中一个列表附加到另一个列表,则您追加的列表类型会在父列表中更改:

>>> type(l)
<class 'multiprocessing.managers.ListProxy'>
>>> type(z)
<class 'multiprocessing.managers.ListProxy'>
>>> l.append(z)
>>> type(l[0])
<class 'list'>   # Not a ListProxy anymore

l[0]z不是同一个对象,并且表现得不像您期望的那样:

>>> l[0].append("hi")
>>> print(z)
[]
>>> z.append("hi again")
>>> print(l[0])
['hi again']

如您所见,更改嵌套列表对ListProxy对象没有任何影响,但更改ListProxy对象确实会更改嵌套列表。文档实际上是explicitly notes this

  

请注意

     

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

通过源代码挖掘,您可以看到当您在ListProxy上调用append时,追加调用实际上是通过IPC发送到管理器对象,然后管理器调用附加在共享列表上。这意味着append的args需要进行pickle / unpickled。在unpickling过程中,ListProxy对象变成一个常规的Python列表,它是ListProxy指向的副本(也就是它的指示对象)。这也是noted in the documentation

  

代理对象的一个​​重要特征是它们是可选择的   它们可以在进程之间传递。但请注意,如果是代理   被发送到相应的经理的过程然后取消它将   产生指示物本身。这意味着,例如,一个共享对象可以包含第二个

所以,回到上面的例子,如果l [0]是z的副本,为什么更新z也会更新l[0]?因为副本也会在Proxy对象中注册,因此,当您更改ListProxy(上例中的z)时,它还会更新列表中的所有已注册副本(l[0]在示例中以上)。但是,副本对代理一无所知,因此当您更改副本时,代理不会更改。

因此,为了使您的示例正常工作,您需要在每次要修改子列表时创建一个新的manager.list()对象,并且只需直接更新该代理对象,而不是通过索引更新它父列表:

#!/usr/bin/python

from multiprocessing import Process, Manager

def worker(x, i, *args):
    sub_l = manager.list(x[i])
    sub_l.append(i)
    x[i] = sub_l


if __name__ == '__main__':
    manager = Manager()
    x = manager.list([[]]*5)
    print x
    p = []
    for i in range(5):
        p.append(Process(target=worker, args=(x, i)))
        p[i].start()

    for i in range(5):
        p[i].join()

    print x

这是输出:

dan@dantop2:~$ ./multi_weirdness.py 
[[0], [1], [2], [3], [4]]
相关问题