为什么Manager()。dict()只更新了一个级别?

时间:2017-01-21 14:09:17

标签: python dictionary multiprocessing python-multiprocessing

我想更新dict中的dict,外部版本通过multiprocessing.Manager()进行实例化:

import multiprocessing

def worker(key, container):
    # this one is not applied
    container['a'][key] = key
    # this one is
    container[key] = 3

if __name__ == "__main__":
    multiprocessing.freeze_support()

    c = multiprocessing.Manager().dict()
    c['a'] = {}

    p1 = multiprocessing.Process(target=worker, args=('x',c,))
    p2 = multiprocessing.Process(target=worker, args=('y',c,))

    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(c.copy())

此代码的输出是

{'y': 3, 'a': {}, 'x': 3}

我希望得到

{'y': 3, 'a': {'x': 'x', 'y': 'y'}, 'x': 3}

dict的第一级可在worker中寻址,但嵌套dict上的操作会被静默丢弃。为什么?

我最初认为这是因为浅拷贝,但使用copy.deepcopy()

时输出相同

3 个答案:

答案 0 :(得分:1)

使用子进程实现Manager,该子进程保留dict对象的本地版本,并提供模仿对象行为的代理函数,而不Manager
当您调用container['a']时,使用代理函数返回与'a'键关联的值的本地副本。因此,如果您在其中一个子流程中修改此副本,则更改仅为本地更改。如果您想要进行全局变更,可以:

  • 修改本地副本并使用您提议的set container['a'] = new_dict的代理进行同步。不方便的是它不是原子的,因此你有一些并发问题,好像p1p2得到字典,都得到{},他们提交的更新将是{{1只考虑其他更新而不考虑其他更新,最后只会注册第二个。

  • 更好的方法是使用嵌套的{key: key}

    Manager.dict

答案 1 :(得分:1)

您应该将Manager的实例传递给子流程,例如

import multiprocessing

def worker(key, container, manager):
    container[key] = manager.dict()  # create a dict using current manager
    container[key][1] = 3
    container[key][2] = manager.dict() # # create a dict using current manager
    container[key][2]['hh'] = 'test'

if __name__ == "__main__":
    with multiprocessing.Manager() as m:
        c = m.dict()
        c['a'] = 1
        p1 = multiprocessing.Process(target=worker, args=('x',c,m))
        p2 = multiprocessing.Process(target=worker, args=('y',c,m))

        p1.start()
        p2.start()
        p1.join()
        p2.join()
        print(c['a'])
        print(c['x'][1])
        print(c['x'][2]['hh'])
        print(c['y'][1])
        print(c['y'][2]['hh'])

输出为

> $ python3.7 ddd.py                                                                                                                                                                                                                 
1
3
test
3
test

答案 2 :(得分:0)

我认为这是a bug

我找到了一种绕过这个问题的深奥方法,我不确定它有多强大。欢迎任何反馈。

import multiprocessing

def worker(key, container):
    add = {key: key}
    container['a'] = dict(container['a'], **add)
    container[key] = 3

if __name__ == "__main__":
    multiprocessing.freeze_support()

    c = multiprocessing.Manager().dict()
    c['a'] = {}

    p1 = multiprocessing.Process(target=worker, args=('x',c,))
    p2 = multiprocessing.Process(target=worker, args=('y',c,))

    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print(c.copy())

此输出

{'y': 3, 'a': {'y': 'y', 'x': 'x'}, 'x': 3}