考虑以下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)使用element wise方法更新列表是可行的,但是比较麻烦,是否有任何建议使其更干净/更快?
答案 0 :(得分:1)
我相信您遇到的问题与您用来创建字典的匿名dic1
对象在字典Manager
中检测到更改有关。
使用+=
运算符更改列表本身不会更改对该列表的 reference -它是相同的列表,只是其中的一个元素已更改(即,第0个元素存储在线程安全字典dic1
中的键'1'
下的列表。
对于dic2
,情况有所不同。与以下行:
self.dic2['1'] = [a[0]+1, a[1], a[2]]
您有效地更新密钥'1'
下存储的值。分配的值是全新列表。它由存储在相同键下的先前值的列表元素组成,但仍然是不同列表。
Manager
对象检测到此类更改,并且在检查dic2
值的过程中对引用进行了无缝更新,以便您可以读取正确的值。
这里的要点如下:
如果键,值或两者均未更改,则线程安全集合(dict
)不会将任何更改传播到其他进程(或线程)。列表是一种引用类型,因此即使列表值更改,值(即引用)也不会更改。