小编典典

共享列表的多处理

python

我写了这样的程序:

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

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

怎么了


阅读 218

收藏
2021-01-20

共1个答案

小编典典

我认为这是由于实施经理的方式有些古怪。

如果创建两个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对象确实会更改嵌套列表。该文档实际上明确指出了这一点

注意

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

append深入研究源代码,您可以看到,当您调用ListProxy时,附加调用实际上是通过IPC发送给管理器对象的,然后管理器调用了在共享列表上的附加。这意味着append需要腌制/未腌制的args
。在取消提取过程中,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

这是输出:

dan@dantop2:~$ ./multi_weirdness.py 
[[0], [1], [2], [3], [4]]
2021-01-20