Flask结合multiprocessing时嵌套装饰器无法执行任务问题
问题原因分析
这问题核心出在multiprocessing的序列化机制上。Python的multiprocessing默认用pickle序列化要传递给子进程的对象(包括函数),而嵌套装饰器返回的内部函数g属于嵌套定义(在另一个函数内部),pickle无法正确序列化这类函数——子进程反序列化时找不到该嵌套函数的定义上下文,导致任务根本没被执行。又因为apply_async默认不主动抛出异常(除非调用get()方法获取结果),所以你看不到任何报错信息。
而简化版装饰器直接返回原函数task,这个函数是模块级别的(定义在other_script模块顶层),pickle能正常识别并序列化,因此子进程可以正确执行任务。
解决办法
1. 用functools.wraps保留原函数元信息
给装饰器的内部函数加上functools.wraps,把原函数的模块、名称等元信息复制到包装函数上,让pickle能识别它关联的模块级原函数:
import functools def test(task): @functools.wraps(task) def g(*args,**kwargs): return task(*args,**kwargs) return g
包装后的函数会保留原函数task的__module__和__name__属性,子进程能通过这些信息找到对应的函数定义。
2. 避免嵌套定义包装函数
把装饰器的包装函数改成模块级别的,结合functools.partial绑定原函数:
from functools import partial def g(task, *args, **kwargs): return task(*args,**kwargs) def test(task): return partial(g, task)
返回的partial对象可被pickle正常序列化,子进程能正确解析执行。
3. 主动获取任务结果排查异常
如果想确认是不是序列化问题,可以在apply_async后调用get()方法,强制捕获子进程可能抛出的异常:
@app.route('/', methods=['POST']) def test(): task = other_script.task results = [] for i in range(3): res = pool.apply_async(task,args=(i,)) results.append(res) # 等待任务完成并获取结果,触发潜在异常 for res in results: res.get() return "Done"
这样如果序列化存在问题,你就能直接看到pickle相关的报错信息,验证之前的推测。
内容的提问来源于stack exchange,提问作者algebruh
相关产品推荐
相关产品推荐

