跨进程共享代理对象实例引发pickle错误问题排查
问题描述
我尝试在Python的多个进程间实现一个简单的共享对象系统,编写了如下代码:
import os from multiprocessing.managers import SyncManager if __name__ == '__main__': manager = SyncManager(authkey=b'test') manager.start() address = manager.address d = manager.dict() pickled_dict = d.__reduce__() pickled_dict[1][-1]["authkey"] = b"test" print(pickled_dict) for i in range(1000): d[i] = i child_id = os.fork() if child_id != 0: # in parent, do work on the proxy object forever i = 0 while True: d[i%1000] = i%3434 i += 1 else: # in children # connect to manager process child_manager = SyncManager(address=address, authkey=b'test') child_manager.connect() # rebuild the dictionary proxy proxy_obj = pickled_dict[0](*pickled_dict[1]) # read on the proxy object forever while True: print(list(proxy_obj.values())[:10])
但在Python 3.9环境中,代码持续触发各类pickle错误,例如_pickle.UnpicklingError: invalid load key, '\x0a'。据我所知Manager对象应支持多进程并发读写,请问我的操作是否存在错误?(注:我已在Python官方仓库提交Issue,目前尚未收到回复)
问题分析与解决
你的核心错误在于手动篡改__reduce__()返回的结果来重建代理对象,这种方式完全不符合multiprocessing.Manager代理对象的设计规范。
__reduce__()是pickle序列化时内部调用的方法,返回的结构属于Python内部实现细节,不同版本可能存在差异,直接修改并调用它创建代理对象会破坏Manager进程与子进程间的通信逻辑,进而引发pickle解析错误。- 正确的做法是在子进程中通过已连接的
child_manager直接获取共享对象,而非手动重建代理。
修改后的子进程代码示例
else: # in children # connect to manager process child_manager = SyncManager(address=address, authkey=b'test') child_manager.connect() # 直接通过连接后的manager获取共享字典代理 proxy_obj = child_manager.dict()
如果需要复用父进程中已初始化的同一个字典,可通过注册自定义共享对象的方式实现:
父进程修改部分
if __name__ == '__main__': manager = SyncManager(authkey=b'test') # 注册共享字典的获取方法 manager.register('get_shared_dict', callable=lambda: d) manager.start() address = manager.address d = manager.dict() # 初始化字典...
子进程修改部分
else: child_manager = SyncManager(address=address, authkey=b'test') # 注册与父进程一致的方法 child_manager.register('get_shared_dict') child_manager.connect() # 获取父进程中创建的同一个共享字典 proxy_obj = child_manager.get_shared_dict()
额外注意事项
os.fork()结合multiprocessing.Manager使用时,Manager本身是独立进程,子进程需通过connect()建立连接后,重新获取代理对象,不能依赖fork前的代理状态。multiprocessing.Manager的代理对象本身具备进程安全特性,支持并发读写,只要通过正规方式获取代理,即可正常实现多进程共享操作。
内容的提问来源于stack exchange,提问作者lezebulon
相关产品推荐
相关产品推荐

