Управляемый список списка не обновляется в многопроцессорной обработке при использовании оператора +=

Рассмотрим следующий код Python:

from multiprocessing import Process, Manager

class MyClass():
    def __init__(self, dic1, dic2):
        self.dic1 = Manager().dict(dic1) # Create a managed dictionary
        self.dic2 = Manager().dict(dic2) # Create a managed dictionary
        process1 = Process(target=self.dictSumOverloaded, args=())
        process2 = Process(target=self.dictSumElementWise, args=())

        process1.start()
        process1.join()

        process2.start()
        process2.join()

    def dictSumOverloaded(self):
        self.dic1['1'][0] += 1 # dic1 is not updated

    def dictSumElementWise(self):
        a = self.dic2['1']
        self.dic2['1'] = [a[0]+1, a[1], a[2]] # dic2 is updated

def main():
    dic1 = {'1': [1, 0, 0]}
    dic2 = {'1': [1, 0, 0]}

    result = MyClass(dic1, dic2)
    print(result.dic1) # Failed
    print(result.dic2) # Success

    # Bypass multiprocessing environment
    dic3 = {'1': [1, 0, 0]}
    dic3['1'][0]+=1
    print(dic3) # Success

if __name__ == '__main__':
    main()

В этом примере я создаю управляемый словарь, содержащий список в качестве атрибута MyClass. Цель состоит в том, чтобы увеличить некоторые элементы этого списка в многопроцессорной среде, но некоторые методы не изменяют список эффективно.

Способ 1:dictSumOverloaded
Перегруженный оператор += используется для увеличения элемента списка на 1, но результат не сохраняется. Диктант не обновляется.
Способ 2:dictSumElementWise
Эта функция создает новый элемент списка на основе старого списка и добавляемых значений. Затем новый список назначается клавише dict. Диктант успешно изменен.
Санитарная проверка: вне многопроцессорной среды
dic3 изменяется, как и ожидалось, при использовании += вне многопроцессорной среды.

Вопросы:
1) Почему += не изменяет элемент списка в многопроцессорной среде?
2) Использование поэлементного метода для обновления списка работает, но громоздко, какие-либо предложения по тому, как сделать его чище/быстрее?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
1 120
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я считаю, что проблема, с которой вы столкнулись, связана с обнаружением изменения в словаре dic1 анонимным Manager объектом, с которым вы его создаете.

Изменение самого списка с помощью оператора += не меняет ссылка на список - это тот же список, просто изменился его элемент (а именно 0-й элемент списка, хранящегося в потокобезопасном словаре dic1 под ключом '1' ).

С dic2 ситуация иная. Со следующей строкой:

self.dic2['1'] = [a[0]+1, a[1], a[2]]

Вы эффективно Обновить значение, хранящееся под ключом '1'. Присвоенное значение представляет собой список совершенно новый. Он состоит из элементов списка, хранящихся как предыдущее значение под тем же ключом, но, тем не менее, это список разные.

Такое изменение обнаруживается объектом Manager, и ссылка в процессе проверки значения dic2 плавно обновляется, чтобы вы могли прочитать правильное значение.

Главное здесь заключается в следующем:

потокобезопасная коллекция (dict) не распространяет никаких изменений на другие процессы (или потоки), если нет изменений в ключах или значениях, или в том и другом. Список является ссылочным типом, поэтому значение (т.е. ссылка) не изменяется, даже если значения списка изменяются.

Спасибо за разъяснение, жаль, что я не понял этого раньше. Является ли мой метод dic2 лучшим способом в этом случае? Я не нашел лучшего/быстрого способа сохранить три значения в этом управляемом словаре и увеличить их.

Shinagan 29.05.2019 18:30

Подождите: документ 3.7 говорит Changed in version 3.6: Shared objects are capable of being nested. For example, a shared container object such as a shared list can contain other shared objects which will all be managed and synchronized by the SyncManager. Значит ли это, что мой код будет правильным в версии 3.6+? Конечно, я только что проверил и использую Python 3.5.6.

Shinagan 29.05.2019 18:42

Я считаю, что это сработает, только если список внутри словаря будет создан как Manager().list.

sophros 29.05.2019 23:07

Может быть, я не пробовал. Если я когда-нибудь попытаюсь использовать += в версии 3.6+ или использовать управляемый список внутри управляемого словаря, я постараюсь не забыть обновить этот комментарий!

Shinagan 31.05.2019 15:08

Другие вопросы по теме