Python多进程中Manager.dict()迭代时对象引用变化问题求助
复现代码
from multiprocessing import Manager class myObject(): #执行操作 manager = Manager() dictio = manager.dict() dictio[(1)] = myObject() print (dictio[(1)]) dictio[(2)] = myObject() print (dictio[(2)]) for key,value in dictio.items(): print (value) print (dictio[key]) print (dictio[(1)]) print (dictio[(2)]) exit()
打印结果
print ( dictio [(1)] )
object at 0x7f1dca56c850
print ( dictio [(2)] )
object at 0x7f1dca56c880
print ( dictio [key] )
object at 0x7f1dcac53af0
object at 0x7f1dca56c130
print ( value )
object at 0x7f1dca56c550
object at 0x7f1dca56c550
print ( dictio [(1)] )
object at 0x7f1dca56c850
print ( dictio [(2)] )
object at 0x7f1dca56c880
疑问
为何循环中print(value)和print(dictio[key])的对象引用,与直接print(dictio[(1)])、print(dictio[(2)])的引用不同?如何在迭代proxydict时保持对象引用一致?
原因
Manager.dict()创建的是代理字典(ProxyDict),它是跨进程通信的代理对象,而非本地普通字典。每次从代理字典中获取对象时,都会通过进程间通信把远程对象序列化/反序列化,返回一个全新的本地副本——这就是内存地址不同的核心原因:你拿到的不是同一个对象实例,而是每次操作生成的新副本。
直接通过dictio[(1)]取时地址看似不变,是因为代理对象内部做了短时间缓存,但迭代items()返回的value是直接反序列化的新副本,循环内再取dictio[key]又是一次新的反序列化操作,所以地址全部不一样。
解决方案
1. 将自定义类注册为可共享的代理对象
让自定义类继承multiprocessing.managers.BaseManager并注册,存入字典的就是对象的代理引用,而非序列化副本,每次获取都会指向同一个远程实例:
from multiprocessing.managers import SyncManager class myObject(): def __init__(self): self.data = 0 def set_data(self, val): self.data = val def get_data(self): return self.data # 注册自定义类到管理器 SyncManager.register('myObject', myObject) if __name__ == '__main__': manager = SyncManager() manager.start() dictio = manager.dict() dictio[1] = manager.myObject() dictio[2] = manager.myObject() print(dictio[1]) print(dictio[2]) for key, value in dictio.items(): print(value) print(dictio[key]) # 验证引用一致:修改value的属性,dictio[key]同步变化 value.set_data(key*10) print(dictio[key].get_data()) print(dictio[1]) print(dictio[2])
这种方式下,所有获取到的value和dictio[key]都是指向同一远程对象的代理,内存地址保持一致,且修改操作会跨进程同步。
2. 本地缓存代理字典中的对象
如果不想修改类结构,可在迭代前把所有对象一次性取到本地字典缓存,后续操作本地缓存的对象即可保证引用一致:
from multiprocessing import Manager class myObject(): def __init__(self): self.id = id(self) manager = Manager() dictio = manager.dict() dictio[1] = myObject() print(dictio[1]) dictio[2] = myObject() print(dictio[2]) # 本地缓存所有对象,避免重复反序列化 local_cache = {k: dictio[k] for k in dictio} for key, value in local_cache.items(): print(value) print(local_cache[key]) # 验证引用一致:id完全相同 print(id(value) == id(local_cache[key])) print(dictio[1]) print(dictio[2])
注意:这种方式下,本地缓存的是对象副本,修改本地对象不会同步到代理字典中,仅适用于无需跨进程同步的场景。
内容的提问来源于stack exchange,提问作者VaraZ

