[python学习笔记] - multiprocessing模块执行带装饰器的方法报错及解决方式
keyword:multiprocessing decorator pickle参考https://segmentfault.com/q/1010000008907475?utm_source=tag-newesthttp://ralph-wang.github.io/blog/2015/02/15/zhuang-shi-qi-yu-duo-jin-cheng-yi-ji-pickle/...
keyword:multiprocessing decorator pickle
参考
https://segmentfault.com/q/1010000008907475?utm_source=tag-newest
http://ralph-wang.github.io/blog/2015/02/15/zhuang-shi-qi-yu-duo-jin-cheng-yi-ji-pickle/
https://stackoverflow.com/questions/9336646/python-decorator-with-multiprocessing-fails
multiprocessing是python常用的多进程模块,可以最大化的发挥机器性能。
经常会计算函数运行耗时,用到装饰器。
但在multiprocessing中运行带装饰器的函数时,就会报错,涉及到了Pickle的问题。
根据参考资料,多进程时,对象及数据是需要序列化反序列化传递的。而python常见的序列化方式就是Pickle。由参考链接可以知道,不是所有的对象(及函数)都可以Pickle序列化。Pickle序列化需要满足几个需求,详情文档。其中比较重要的一个就是,被序列化的对象要在模块顶层定义。
例如我们常见的计时装饰器方法如下:
def time_elapse(fn):
def _wrapper(*args, **kwargs):
start = time.perf_counter_ns()
fn(*args, **kwargs)
print(f"{fn.__name__} cost {(time.perf_counter_ns() - start)/1_000_000_000} s")
return _wrapper
@time_elapse
def f():
print("f")
f.__dict__
# 输出为{}
由于f
的__dict__
属性在被装饰器修饰后,并不在顶层,所以无法通过__dict__
属性正确反序列化。
此时我们换另一种方式,使用类装饰器,写法如下:
class TimeElapse(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
start = time.perf_counter_ns()
self.func(*args, **kwargs)
print(
f"{self.func.__name__} cost {(time.perf_counter_ns() - start)/1_000_000_000} s")
def f():
print("f")
f1 = TimeElapse(f)
f1.__dict__
# 输出为{'func': <function __main__.f()>}
由此可知,经过类装饰器的修饰后,__dict__
是可以准确的存储并还原信息的,所以Pickle序列化能成功。
结论
multiprocessing的进程操作设计Pickle序列化,而函数装饰器的结构特点导致Pickle序列化失败。通过改用类装饰器,可以解决Pickle序列化失败的问题。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)