为何设置recurse=True会导致dill不识别函数中的全局变量?
dill序列化含全局变量函数时recurse=True导致加载后全局变量未定义的原因分析
正常运行场景
以下代码可正常执行,子进程通过initializer设置全局变量foo,worker函数能正确读取该变量:
import multiprocessing import dill def initializer(): global foo foo = 1 def worker(arg): return foo with multiprocessing.Pool(2, initializer) as pool: res = pool.map(worker, range(10)) print(res)
输出结果:[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
序列化后出错的场景
当使用dill.dump(..., recurse=True)序列化函数并重新加载后,运行会抛出NameError:
import multiprocessing import dill def initializer(): global foo foo = 1 def worker(arg): return foo with open('funcs.pkl', 'wb') as f: dill.dump((initializer, worker), f, recurse=True) with open('funcs.pkl', 'rb') as f: initializer, worker = dill.load(f) with multiprocessing.Pool(2, initializer) as pool: res = pool.map(worker, range(2))
错误信息:
File "/tmp/ipykernel_158597/1183951641.py", line 9, in worker return foo ^^^ NameError: name 'foo' is not defined
原因分析
核心在于dill的recurse参数对函数全局命名空间的处理逻辑:
- 当
recurse=False时:dill仅序列化函数本身的字节码和基本元数据,不会递归捕获函数引用的全局上下文。加载后,函数仍然在当前进程/子进程的全局命名空间中执行,initializer在子进程中设置的全局变量foo会直接进入worker的运行命名空间,因此能被正常读取。 - 当
recurse=True时:dill会递归序列化函数的整个执行上下文,包括函数绑定的原始全局命名空间快照。加载后,worker函数的全局变量引用被固定到序列化时的空命名空间(此时foo还未定义)。而multiprocessing的子进程中,initializer是在子进程的全局命名空间中设置foo,这个命名空间和worker绑定的序列化快照命名空间完全隔离,因此worker找不到foo,抛出NameError。
简单来说,recurse=True把函数和它的“原始环境”绑定死了,后续子进程中动态设置的全局变量无法进入这个绑定的环境,导致变量未定义。
内容的提问来源于stack exchange,提问作者quant
相关产品推荐
相关产品推荐

