共享列表的多处理

Eri*_* Xu 12 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
Run Code Online (Sandbox Code Playgroud)

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

出了什么问题?

dan*_*ano 12

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

如果您创建两个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
Run Code Online (Sandbox Code Playgroud)

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

>>> l[0].append("hi")
>>> print(z)
[]
>>> z.append("hi again")
>>> print(l[0])
['hi again']
Run Code Online (Sandbox Code Playgroud)

如您所见,更改嵌套列表对ListProxy对象没有任何影响,但更改ListProxy对象确实会更改嵌套列表.文档实际上明确指出:

注意

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

通过源代码挖掘,您可以看到当您调用appendListProxy时,追加调用实际上是通过IPC发送到管理器对象,然后管理器调用附加在共享列表上.这意味着append需要进行酸洗/去除的args .在unpickling过程中,ListProxy对象变成一个常规的Python列表,它是ListProxy指向的副本(也就是它的指示对象).这在文档中有说明:

代理对象的一个​​重要特性是它们是可选择的,因此它们可以在进程之间传递.但请注意,如果将代理发送到相应管理器的进程,那么取消对其进行取消将生成所指对象.这意味着,例如,一个共享对象可以包含第二个

那么,回到上面的例子,如果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
Run Code Online (Sandbox Code Playgroud)

这是输出:

dan@dantop2:~$ ./multi_weirdness.py 
[[0], [1], [2], [3], [4]]
Run Code Online (Sandbox Code Playgroud)