不能直接用 pickle.dumps() 传给子进程,因它不保证跨进程安全:含线程锁、文件句柄等对象会报 TypeError;模块路径或类定义不一致会导致反序列化失败;应只序列化纯数据结构,确保子进程能正确导入模块,并优先使用 multiprocessing.Queue 安全传输。

为什么不能直接用 pickle.dumps() 传给子进程?
因为 pickle 序列化本身不保证跨进程安全——如果被序列化的对象里含线程锁、文件句柄、数据库连接或 lambda 函数,dumps() 会直接报 TypeError: can't pickle XXX objects。更隐蔽的问题是:主进程和子进程的模块导入路径、类定义版本不一致时,反序列化会失败(AttributeError 或 ModuleNotFoundError),尤其在使用相对导入或热重载开发环境时。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 只对纯数据结构(
dict、list、namedtuple、自定义类但不含可变状态)做pickle; - 避免序列化
threading.Lock、socket.socket、open()返回的文件对象; - 确保子进程中已提前
import所有被序列化对象所属的模块,且路径与主进程完全一致(比如都用绝对导入); - 用
pickle.HIGHEST_PROTOCOL提升效率和兼容性,但注意 Python 3.8+ 默认协议已是 5,老版本需显式指定。
用 multiprocessing.Queue 传 pickle 数据最稳
multiprocessing.Queue 内部就是靠 pickle 做序列化,但它自动处理了管道创建、锁同步和异常边界,比手动写 os.pipe() + os.write() 安全得多。唯一要注意的是它不支持非阻塞读写,且默认有大小限制(maxsize=0 表示无上限,但实际受内存约束)。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 不要把大对象(如 >100MB 的
numpy.ndarray)直接塞进Queue,会触发大量内存拷贝,改用multiprocessing.shared_memory或文件中转; - 子进程取数据前先用
queue.empty()判断可能有竞态,应改用queue.get(timeout=1)配合异常捕获; - 若需传函数逻辑,必须确保函数定义在模块顶层(不能是嵌套函数或
lambda),且模块可被子进程 import —— 否则反序列化时找不到该函数。
spawn 启动方式下,pickle 失败的典型错误及修复
Windows 和 macOS 默认用 spawn 启动子进程(Linux 可配),这意味着子进程会重新导入主模块。此时常见错误是:AttributeError: Can't get attribute 'MyClass' on <module '__main__' from '...'。根本原因是主脚本被当作 __main__ 运行,而子进程导入时找不到同名模块。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 把要被序列化的类/函数定义移到独立的
.py文件中(如data_structs.py),主脚本只做import data_structs; - 主脚本入口加
if __name__ == '__main__':保护,防止子进程重复执行初始化代码; - 调试时在子进程中打印
sys.path和__name__,确认模块加载路径是否和主进程一致; - 临时测试可用
set_start_method('fork')(仅 Linux),但生产环境别依赖它,因fork有内存快照风险且不可移植。
替代方案:什么时候该放弃 pickle?
当数据含不可序列化字段(如带方法的类实例)、需要跨语言(Go/JS 消费)、或追求更高性能时,pickle 就成了瓶颈。这时候 json(纯文本、安全但慢)、msgpack(二进制、快、跨语言)、或 protobuf(强 schema、压缩率高)更合适。但注意:json 不支持 bytes 和 datetime,得自己写 encoder/decoder。
实操建议:
立即学习“Python免费学习笔记(深入)”;
- 传输日志、配置、简单 API 响应,优先用
json.dumps(obj, default=str),子进程用json.loads(); - 高频小数据(如传感器采样点),用
msgpack.packb(obj, use_bin_type=True),比pickle快 2–3 倍且无执行风险; - 若已有 protobuf schema,生成 Python 类后用
.SerializeToString(),体积比pickle小 40%+; - 永远别用
pickle解析不受信来源的数据 —— 它能执行任意代码,等同于远程代码执行漏洞。
真正难的不是怎么序列化,而是搞清哪些东西不该放进序列化流里 —— 比如一个 logging.Logger 实例看着像普通对象,但里面藏着线程局部存储和 I/O 缓冲区,一 pickle 就崩。


















