Python多进程场景下出现TypeError: cannot pickle '_cffi_backend.FFI' object错误的咨询
首先直接给结论:cffi本身和multiprocessing没有本质的兼容性冲突,但在Windows环境下,由于multiprocessing的实现机制,加上cffi的_cffi_backend.FFI对象无法被pickle序列化,会导致你遇到的这个错误。
错误原因详解
Windows系统下,Python的multiprocessing默认使用spawn模式创建子进程——这种模式不会像Linux的fork那样直接复制父进程的内存空间,而是会启动全新的Python解释器,然后把需要的任务对象(包括函数、参数)通过pickle序列化后传递给子进程。
而你代码中的action函数大概率引用了父进程中初始化的FFI对象(比如全局的ffi实例),而_cffi_backend.FFI是C扩展实现的对象,不支持pickle序列化,所以在进程池尝试把任务序列化发送给子进程时,就触发了TypeError: cannot pickle '_cffi_backend.FFI' object错误。单进程模式下不需要序列化传递对象,所以不会有问题。
解决方案
核心思路是:不要让父进程的FFI对象被传递到子进程,而是让每个子进程自己初始化需要的cffi资源。这里提供两种常见的实现方式:
1. 在子进程任务函数内初始化FFI
直接把cffi的初始化逻辑放到action函数内部,这样每个子进程启动后都会自己创建FFI实例,不需要从父进程传递:
def action(box, var1, var2, var3, var4): # 子进程内独立初始化cffi from cffi import FFI ffi = FFI() # 这里添加你的cffi配置逻辑,比如加载动态库、定义C函数等 # 示例:ffi.cdef("void some_function();") # 示例:lib = ffi.dlopen("your_library.dll") # 执行原本的业务逻辑 processed_box = your_business_logic(box, var1, var2, var3, var4, ffi, lib) return processed_box
2. 使用进程池的初始化函数统一配置
如果多个任务函数都需要用到cffi资源,可以用multiprocessing.Pool的initializer参数,在每个子进程启动时统一初始化cffi:
# 定义子进程初始化函数 def init_worker(): # 全局变量让子进程内的函数可以访问 global ffi, lib from cffi import FFI ffi = FFI() # 统一配置cffi,比如加载库、定义接口 ffi.cdef("void some_function();") lib = ffi.dlopen("your_library.dll") def action(box, var1, var2, var3, var4): # 直接使用子进程内已初始化的ffi和lib lib.some_function() # 业务逻辑处理 return processed_box if parallel_process is True: print("Start parallel processing") # 传入initializer,每个子进程启动时执行init_worker with multiprocessing.Pool(initializer=init_worker) as pool: boxes = pool.starmap( action, zip( boxes, repeat(var1), repeat(var2), repeat(var3), repeat(var4), ), )
你找到的那份PR本质也是采用了类似的思路——避免序列化父进程的cffi对象,转而在子进程中重新初始化相关资源,这样就绕开了pickle无法处理C扩展对象的问题。
内容的提问来源于stack exchange,提问作者HuLu ViCa

