函数内使用多进程填充数组遇Pickle错误的解决建议咨询
解决嵌套函数在joblib多进程/线程中的Pickle错误
你碰到的这个AttributeError本质原因是:Python的标准Pickle模块无法序列化定义在其他函数内部的本地嵌套函数,而joblib在调度任务时需要把函数对象传递给工作进程/线程,所以会触发这个错误。下面给你两种可行的解决方案,根据你的需求选择:
方案1:把嵌套函数移到全局作用域(最推荐,无额外依赖)
这是最简单直接的方法,把原本嵌套在processInput里的testNested函数移到全局空间,同时把要修改的numpy数组作为参数传入函数。因为你用的是threading后端,线程共享同一内存空间,所以修改会直接作用在原数组上:
import multiprocessing import numpy as np from joblib import Parallel, delayed # 将函数移至全局作用域,避免Pickle问题 def testNested(t, arr): arr[t] = t def processInput(): num_cores = multiprocessing.cpu_count() a = np.zeros(10) # 调用Parallel时传入数组作为参数 Parallel(n_jobs=num_cores, backend="threading")( delayed(testNested)(t, a) for t in range(10) ) return a # 测试执行 if __name__ == "__main__": result = processInput() print(result) # 输出 [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
方案2:保留嵌套函数结构,使用支持本地函数序列化的后端
如果你必须保留嵌套函数(比如需要直接访问processInput内的变量,不想通过参数传递),可以使用joblib的loky后端(默认多进程后端),它依赖cloudpickle来序列化本地函数。注意:如果用多进程(loky),需要用共享内存来让子进程修改同一个numpy数组,否则每个进程会操作自己的数组拷贝,原数组不会变化:
import multiprocessing import numpy as np from joblib import Parallel, delayed def processInput(): # 创建共享内存的numpy数组,让所有子进程能修改同一内存区域 shared_buffer = multiprocessing.Array('d', 10) # 'd'表示双精度浮点数类型 a = np.frombuffer(shared_buffer.get_obj(), dtype=np.float64) a[:] = 0 # 初始化数组为0 # 保留嵌套函数结构 def testNested(t): # 子进程中重新获取共享内存的数组视图 arr = np.frombuffer(shared_buffer.get_obj(), dtype=np.float64) arr[t] = t # 使用loky后端(默认),它会自动用cloudpickle序列化本地函数 Parallel(n_jobs=multiprocessing.cpu_count())( delayed(testNested)(t) for t in range(10) ) return a # 测试执行 if __name__ == "__main__": result = processInput() print(result) # 输出 [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
补充说明:
- 如果你坚持用
threading后端又想保留嵌套函数,需要确保joblib使用cloudpickle作为序列化器。可以通过设置joblib.register_parallel_backend或者在Parallel中指定配置序列化器,不过这种场景下方案1会更简洁。 - 对于简单的数组赋值任务,
threading后端足够高效,不需要额外处理共享内存问题,操作成本更低。
内容的提问来源于stack exchange,提问作者Shirley Hou
相关产品推荐
相关产品推荐

