You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

跨进程共享代理对象实例引发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()

额外注意事项

  1. os.fork()结合multiprocessing.Manager使用时,Manager本身是独立进程,子进程需通过connect()建立连接后,重新获取代理对象,不能依赖fork前的代理状态。
  2. multiprocessing.Manager的代理对象本身具备进程安全特性,支持并发读写,只要通过正规方式获取代理,即可正常实现多进程共享操作。

内容的提问来源于stack exchange,提问作者lezebulon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 17:45:29