首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在multiprocessing.Manager对象中创建和更新嵌套字典和列表

在multiprocessing.Manager对象中创建和更新嵌套字典和列表
EN

Stack Overflow用户
提问于 2022-08-18 20:37:05
回答 1查看 263关注 0票数 1

我已经在multiprocessing.Manager.dict对象中创建了一个嵌套字典。当我将字典方法应用到嵌套字典中时,像updateclear等字典方法不起作用。这就是一个例子:

代码语言:javascript
运行
复制
from multiprocessing import Manager, Process


def worker(shared_object):
    print(f'Before defining the nested dictionary: {shared_object}')
    shared_object['nested'] = {
        'first_key': 'first_value'
    }
    print(f'After defining the nested dictionary: {shared_object}')
    shared_object['nested'].update(
        {
            'second_key': 'second_value'
        }
    )
    print(f'After updating the nested dictionary: {shared_object}')


if __name__ == '__main__':
    with Manager() as manager:
        shared_dict = manager.dict()
        worker_one = Process(target=worker, args=(shared_dict,))
        worker_one.start()
        worker_one.join()

这就是结果:

代码语言:javascript
运行
复制
Before defining the nested dictionary: {}
After defining the nested dictionary: {'nested': {'first_key': 'first_value'}}
After updating the nested dictionary: {'nested': {'first_key': 'first_value'}}

我能做什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-19 14:35:48

共享字典的用法与以下内容相同,可以重写如下:

代码语言:javascript
运行
复制
temp = shared_object['nested']
temp.update({'second_key': 'second_value'})

当您从共享字典请求nested键的值时,它将存储在当前进程的内存空间中。这意味着,您对此字典所做的任何更改都不会反映在存储在管理器中的共享字典中,因为管理器无法知道在检索字典的子集后对字典做了什么。因此,您需要做的是通知经理您已经对嵌套字典进行了更改。有两种方法可以做到这一点:

使用更新的值显式调用manager对象。

在这里,在对嵌套字典进行任何更改之后,这可能是最简单的(也是性能上最快的)修复,显式地将键的值设置为修改过的字典:

代码语言:javascript
运行
复制
temp = shared_object['nested']
temp.update({'second_key': 'second_value'})
shared_object['nested'] = temp

这样做将让经理知道托管对象中发生了更改。

为嵌套字典创建另一个托管对象(手动vs自动)

实现这一点的另一种方法是为嵌套字典创建托管对象并将其存储起来。但是,请记住,如果对象的嵌套级别非常深,这会很快变得不方便。这是因为对于每个嵌套字典,您需要创建另一个托管对象。此外,如果您创建的子进程是守护进程(在使用multiprocessing.Pool时是默认的),并且它们需要在共享字典中添加自己的嵌套对象,则这将无法工作。这是因为要创建一个托管对象,您需要启动一个管理器进程,而守护进程不能生成它们自己的进程。

但是,如果让共享字典负责,如果有人试图在共享字典中存储嵌套字典,那么这两个问题都可以很快得到解决。如果有人试图在共享字典中存储嵌套字典,那么这两个问题都可以很快得到解决。下面给出了一个示例,说明如何做到这一点(它适用于列表和字典):

代码语言:javascript
运行
复制
from multiprocessing.managers import SyncManager, MakeProxyType, ListProxy, State
from multiprocessing import Process, Lock
from collections import UserDict
from collections import UserList


class ManagerList(UserList):

    def __check_state(self):
        global manager
        global lock

        # Managers are not thread-safe, protect starting one from within another with a lock
        with lock:
            if manager._state.value != State.STARTED:
                manager.start(initializer=init)

    def __setitem__(self, key, value):
        global manager
        self.__check_state()

        if isinstance(value, list):
            value = manager.list(value)
        elif isinstance(value, dict):
            value = manager.dict(value)
        return super().__setitem__(key, value)


class ManagerDict(UserDict):

    def __check_state(self):
        global manager
        global lock

        # Managers are not thread-safe, protect starting one from within another with a lock
        with lock:
            if manager._state.value != State.STARTED:
                manager.start(initializer=init)

    def __setitem__(self, key, value):
        global manager
        self.__check_state()

        if isinstance(value, list):
            value = manager.list(value)
        elif isinstance(value, dict):
            value = manager.dict(value)
        return super().__setitem__(key, value)


ManagerDictProxy = MakeProxyType('DictProxy', (
    '__contains__', '__delitem__', '__getitem__', '__iter__', '__len__',
    '__setitem__', 'clear', 'copy', 'get', 'items',
    'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'
    ))
ManagerDictProxy._method_to_typeid_ = {
    '__iter__': 'Iterator',
    }

SyncManager.register('list', ManagerList, ListProxy)
SyncManager.register('dict', ManagerDict, ManagerDictProxy)


def init():
    global manager
    global lock

    lock = Lock()
    manager = SyncManager()

def worker(shared_object):
    print(f'Before defining the nested dictionary: {shared_object}')
    shared_object['nested'] = {
        'first_key': 'first_value',
    }
    print(f'After defining the nested dictionary: {shared_object["nested"]}')
    shared_object['nested'].update(
        {
            'second_key': 'second_value'
        }
    )
    print(f'After updating the nested dictionary: {shared_object["nested"]}')


if __name__ == "__main__":
    manager = SyncManager()
    manager.start(initializer=init)

    d = manager.dict()  # It works with manager.list() too
    p = Process(target=worker, args=(d, ))
    p.start()
    p.join()

这使用__setitem__ dunder方法来检查分配给字典的值是另一个列表还是一个字典,如果是,它会为它们创建一个托管对象并将其存储起来。现在对这些嵌套字典/列表所做的任何更改也将反映在原始字典中。这适用于任意数量的嵌套级别。

但是,请记住,只有当数据类型为listdict (或它们的子类)时,才会创建托管对象。此外,它还为每个嵌套级别创建了一个管理流程。因此,使用第一种方法总是比使用这个方法更快,尽管这个方法更方便。

输出

代码语言:javascript
运行
复制
Before defining the nested dictionary: {}
After defining the nested dictionary: {'first_key': 'first_value'}
After updating the nested dictionary: {'first_key': 'first_value', 'second_key': 'second_value'}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73409151

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档