1

Я хотел обновить dict в dict, снаружи один из них инстанцирован через multiprocessing.Manager():Почему Manager(). Dict() обновляется только на одном уровне?

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()

ответ

1

Manager реализуется с использованием подпроцесс, которые держат локальную версию dict объекта и обеспечить прокси-функции, которые имитируют поведение объекта без Manager.
Когда вы вызываете container['a'], вы используете функцию прокси-сервера, которая возвращает локальную копию значения, связанного с ключом 'a'. Таким образом, если вы измените эту копию в одном из ваших подпроцессов, изменение будет только локальным. Если вы хотите, глобальное изменение, вы можете:

  • Измените локальную копию и синхронизировать с помощью прокси-сервер set, т.е.container['a'] = new_dict, как вы предложили. Неудобно то, что он не является атомарным и, следовательно, у вас есть проблемы с параллелизмом, как если бы p1 и p2 получили словарь, оба получат {}, а обновление, которое они отправляют, будет только {key: key}, без учета другого обновления и только второго будут зарегистрированы в конце.

  • Лучше всего было бы использовать вложенную Manager.dict:

    import multiprocessing 
    
    
    def worker(key, container): 
        container['a'][key] = key 
        container[key] = 3 
    
    
    if __name__ == "__main__": 
        multiprocessing.freeze_support() 
    
        manager = multiprocessing.Manager() 
        container = manager.dict() 
        container['a'] = manager.dict() 
    
        p1 = multiprocessing.Process(target=worker, args=('x', container)) 
        p2 = multiprocessing.Process(target=worker, args=('y', container)) 
    
        p1.start() 
        p2.start() 
        p1.join() 
        p2.join() 
        print(container.copy()) 
        print(container['a'].copy()) 
    
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} 
Смежные вопросы