在Python多进程模块中使用Manager()共享数据

8 投票
1 回答
3283 浏览
提问于 2025-04-17 08:13

我在使用 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(代理):对包含的容器的更新不会被管理器注意到,因此不会有你预期的变化。请注意,字典本身在第二个例子中会被更新,但你看不到,因为管理器没有同步。

撰写回答