为何multiprocessing使用fork启动方式时Pool.map传入的函数仍需pickle序列化
关于Linux下multiprocessing模块fork模式仍需pickle序列化的解答
首先纠正一个常见认知误区:fork仅会克隆调用fork时间点之前的父进程全部状态,fork完成后父子进程的地址空间完全独立,父进程后续新增的函数、变量都不会同步到子进程中。在此基础上,fork模式仍要求序列化主要有三个原因:
- 跨启动方式的统一架构设计
multiprocessing模块需要同时兼容fork、spawn、forkserver三种进程启动方式,为了降低维护成本,任务分发层做了通用抽象:不管使用哪种启动方式,提交的任务(包括目标函数、入参)都会先走pickle序列化,再通过内部IPC队列传递给工作进程,工作进程统一反序列化后执行,不会单独为fork模式做跳过序列化的特殊适配。 - 进程池预启动机制的客观需求
绝大多数场景下我们使用的multiprocessing.Pool进程池都会提前fork出固定数量的工作进程,后续提交的任务函数很多是在工作进程启动之后才定义、生成的,预启动的工作进程内存中根本不存在这些函数的定义,只能通过序列化传递才能让工作进程拿到可执行的函数对象。
举个最简单的可复现例子:
from multiprocessing import Pool def func1(x): return x * 2 if __name__ == "__main__": # 此处已经完成2个工作进程的fork,子进程内存中仅存在func1的定义 pool = Pool(processes=2) # func2是fork完成后才在父进程定义的,子进程内存中没有该函数 def func2(x): return x * 3 # 必须依赖pickle序列化把func2传递给子进程才能正常执行 print(pool.map(func2, [1,2,3]))
- 避免地址空间访问的安全问题
就算目标函数在fork之前已经存在,父子进程的地址空间完全独立,工作进程也不会直接读取父进程的内存空间获取任务函数,走IPC传递序列化后的对象是多进程通信的标准实现,能避免跨地址空间访问带来的各种内存安全、同步问题。
内容的提问来源于stack exchange,提问作者Jorge E. Cardona
相关产品推荐
相关产品推荐

