我认为这是由于实施经理的方式有些古怪。
如果创建两个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/pythonfrom multiprocessing import Process, Managerdef worker(x, i, *args): sub_l = manager.list(x[i]) sub_l.append(i) x[i] = sub_lif __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]]



