使用 += 运算符时,列表的托管字典未在多处理中更新

Managed dict of list not updated in multiprocessing when using += operator

考虑以下 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 属性的托管字典。目标是在多处理环境中增加这个列表的一些元素,但是有些方法并没有有效地修改列表。

方法一:dictSumOverloaded
重载运算符 += 用于将列表的元素递增 1,但结果不会持续存在。字典未更新。
方法二:dictSumElementWise
此函数基于旧列表和要添加的值明智地创建一个新的列表元素。然后将新列表分配给字典键。字典修改成功
健全性检查:在多处理环境之外
dic3 在多处理环境外使用 += 时按预期修改。

问题:
1) 为什么 += 不修改多处理环境中的列表元素?
2) 使用元素明智的方法来更新列表,但很麻烦,有什么建议吗cleaner/faster?

我相信您遇到的问题与检测到字典 dic1 中的更改有关,您创建它的匿名 Manager 对象。

使用 += 运算符更改列表本身不会将 reference 更改为列表 - 它是同一个列表,只是其中的一个元素发生了变化(即存储在 thread-safe 字典 dic1 中键 '1') 下的列表的第 0 个元素。

dic2情况不同。使用以下行:

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

您实际上更新 存储在键 '1' 下的值。分配的值是一个 全新的 列表。它由存储为同一键下的先前值的列表元素组成,但它仍然是一个 不同 列表。

这样的变化被 Manager 对象检测到,并且在您检查 dic2 值的过程中的引用被无缝更新,以便您可以读取正确的值。

这里的重点是:

如果键和/或值没有更改,thread-safe 集合 (dict) 不会将任何更改传播到其他进程(或线程)。列表是引用类型,因此即使列表值发生变化,值(即引用)也不会改变。