Управляемый элемент списка не обновляется в многопроцессорной среде при использовании оператора + = - PullRequest
1 голос
/ 29 мая 2019

Рассмотрим следующий код 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) Использование поэлементного метода для обновления списка работает, но обременительно, есть какие-либо предложения по его более чистому / быстрому?

1 Ответ

1 голос
/ 29 мая 2019

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

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

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

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

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

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

Главное здесь следующее:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...