在Python多进程模块中使用Manager()共享数据
我在使用 multiprocessing
模块(python 2.7,Linux)共享数据时,发现稍微不同的代码会产生不同的结果:
import os
import time
from multiprocessing import Process, Manager
def editDict(d):
d[1] = 10
d[2] = 20
d[3] = 30
pnum = 3
m = Manager()
第一种版本:
mlist = m.list()
for i in xrange(pnum):
mdict = m.dict()
mlist.append(mdict)
p = Process(target=editDict,args=(mdict,))
p.start()
time.sleep(2)
print 'after process finished', mlist
这个版本的结果是:
进程结束后 [{1: 10, 2: 20, 3: 30}, {1: 10, 2: 20, 3: 30}, {1: 10, 2: 20, 3: 30}]
第二种版本:
mlist = m.list([m.dict() for i in xrange(pnum)]) # main difference to 1st version
for i in xrange(pnum):
p = Process(target=editDict,args=(mlist[i],))
p.start()
time.sleep(2)
print 'after process finished', mlist
这个版本的结果是:
进程结束后 [{}, {}, {}]
我不明白为什么结果差别这么大。
1 个回答
11
这是因为你第二次是通过列表的索引来访问变量,而第一次是直接传递了实际的变量。正如多进程文档中所说的:
对可变值或字典和列表代理中的项目的修改不会通过管理器传播,因为代理无法知道它的值或项目何时被修改。
这意味着,为了跟踪在容器(字典或列表)中被改变的项目,你必须在每次编辑后重新赋值。考虑以下的修改(为了说明,我并不是说这段代码很优雅):
def editDict(d, l, i):
d[1] = 10
d[2] = 20
d[3] = 30
l[i] = d
mlist = m.list([m.dict() for i in xrange(pnum)])
for i in xrange(pnum):
p = Process(target=editDict,args=(mlist[i], mlist, i,))
p.start()
如果你现在打印mlist
,你会发现它的输出和你第一次尝试时是一样的。重新赋值将允许容器代理再次跟踪更新的项目。
在这种情况下,你的主要问题是你在一个list
代理中有一个dict
(代理):对包含的容器的更新不会被管理器注意到,因此不会有你预期的变化。请注意,字典本身在第二个例子中会被更新,但你看不到,因为管理器没有同步。