【问题标题】:Multiprocessing Manager().dict() update failedMultiprocessing Manager().dict() 更新失败
【发布时间】:2020-01-15 13:01:14
【问题描述】:

我尝试像这样更新Manager().dict

from multiprocessing import Process, Manager

def f(d):
    print(d)
    d['a'][1] = 'something new'
    print(d)


if __name__ == '__main__':
    manager = Manager()

    d = manager.dict()
    d['a'] = {1: 3}

    print(d)

    p1 = Process(target=f, args=(d,))

    p1.start()
    p1.join()

    print(d)

但是,它会打印出来

{'a': {1: 3}}
{'a': {1: 3}}
{'a': {1: 3}}
{'a': {1: 3}}

我尝试d['a'] = Manager().dict({1: 3}),它可以打印预期的输出,但是当字典非常复杂时,这种解决方法非常低效。

我怎样才能像普通的dict 一样轻松更新Manager().dict

【问题讨论】:

  • 似乎是 python bugs.python.org/issue6766 中的一个错误,您无法修改嵌套字典 d['a'] = {1: 'something new'} 虽然在 f(d) 中有效
  • @DeveshKumarSingh 是的,这是一个讨厌的错误。

标签: python multiprocessing python-multiprocessing


【解决方案1】:

您并没有真正更新Manager.dict,而是其中的常规dict,这不会传播。

来自docs

如果标准(非代理)列表或字典对象包含在 引用,对这些可变值的修改将不会被传播 通过经理,因为代理无法知道何时 其中包含的值被修改。但是,将值存储在 容器代理(在代理对象上触发 setitem) 确实通过管理器传播,因此可以有效地修改此类 一个项目,可以将修改后的值重新分配给容器 代理...

因此,重新分配修改后的嵌套常规 dict 即可:

from multiprocessing import Process, Manager, current_process


def f(d):
    print(f"{current_process().name}: {d}")
    a = d['a']
    a[1] = 'something new'
    d['a'] = a
    print(f"{current_process().name}: {d}")


if __name__ == '__main__':

    with Manager() as m:
        d = m.dict()
        d['a'] = {1: 3}    
        print(f"{current_process().name}: {d}")

        p1 = Process(target=f, args=(d,))    
        p1.start()
        p1.join()

        # convert to regular dictionary before shutting down the
        # manager-process
        d = dict(d)

    print(f"{current_process().name}: {d}")

输出:

MainProcess: {'a': {1: 3}}
Process-2: {'a': {1: 3}}
Process-2: {'a': {1: 'something new'}}
MainProcess: {'a': {1: 'something new'}}

Process finished with exit code 0

对于 Python 3.6+,也可以嵌套代理:

from multiprocessing import Process, Manager, current_process
from multiprocessing.managers import DictProxy


def f(d):
    print(f"{current_process().name}: {unproxy_dict(d)}")
    d['a'][1] = 'something new'
    print(f"{current_process().name}: {unproxy_dict(d)}")


def unproxy_dict(dict_proxy):
    return {k: (dict(v) if isinstance(v, DictProxy) else v)
            for k, v in dict_proxy.items()}


if __name__ == '__main__':

    with Manager() as m:
        d = m.dict()
        d['a'] = m.dict({1: 3})  # nested Manager.dict
        print(f"{current_process().name}: {unproxy_dict(d)}")

        p1 = Process(target=f, args=(d,))    
        p1.start()
        p1.join()

        # convert to regular dictionaries before shutting down the
        # manager-process
        d = unproxy_dict(d)

    print(f"{current_process().name}: {d}")

【讨论】:

  • 所以我们还是应该嵌套DictProxy或者创建Manager().dict()来避免这个bug?对?如果Manager().dict() 的一个实例调用.clear(),它会释放内存吗?
  • @GoingMyWay 是的,您可以使用这两种方法中的任何一种,如两个代码 sn-ps 中所示。如果您没有对嵌套对象的任何其他引用,.clear() 应该最终释放内存 (gc)。请记住,Python 中的容器只保存对其他对象的引用,而不是实际值。
  • 谢谢。我尝试了d['a'] = m.dict({1: [1, 2]}) 并在f 中执行d['a'][1] = 'new value,仍然无法更改值,所以我认为我需要使用嵌套的Manager().list()
  • @GoingMyWay 如果您打算将[1, 2] 换成new value,您也可以使用a = d['a']; a[1] = 'something new'; d['a'] = a。只有第一个字典是 manager-dict,就像在第一个例子中一样。结果将是{'a': {1: 'something new'}}
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-03
  • 2023-02-10
  • 2018-05-07
  • 2012-12-23
  • 2022-01-17
  • 2014-03-01
相关资源
最近更新 更多