1

考虑以下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()

在此示例中,我创建了一个托管 dict,其中包含一个列表作为MyClass. 目标是在多处理环境中增加此列表的某些元素,但某些方法不能有效地修改列表。

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

问题
1)为什么+=不在多处理环境中修改列表元素?
2)使用元素明智的方法来更新列表工作但很麻烦,有什么建议让它更干净/更快?

4

1 回答 1

1

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

使用运算符更改列表本身+=不会更改对列表的引用dic1- 它是相同的列表,只是其中的一个元素发生了变化(即存储在key 下的线程安全字典中的列表的第 0 个元素'1')。

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

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

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

对象会检测到此类更改Manager,并且您检查值的过程中的引用dic2会无缝更新,以便您可以读取正确的值。

这里的要点如下:

如果键、值或两者都没有更改,线程安全集合 ( dict) 不会将任何更改传播到其他进程(或线程)。List 是一种引用类型,因此即使列表值发生更改,值(即引用)也不会更改。

于 2019-05-29T16:17:07.603 回答