彻底解决python多进程can‘t pickle问题

原因分析:

这个问题,出在主进程向新进程(子进程)传递的参数不可被序列化上,那么,只要传递的参数可以序列化,问题就不存在了(貌似是废话,但理解这句话至关重要)。

假设不可被pickle的类是Task,而task是一个不可被pickle的Task的实例化对象。

不论使用multiprocessing.Process()还是multiprocessing.Pool(),都是主进程向子进程的worker传递参数,这些参数要确保是可序列化的即可。

  • 错误方式:先将task创建完成,再由主进程提供给子进程的worker运行,就会报错can’t pickle
  • 正确方式:将创建task这一动作,放在worker中,而向worker传递的参数(即worker接受的参数)是Python的内置类型,如字符串

总结要点:

  1. worker(args)要创建在if __name__ == __main__之外,确保是在顶层(通俗理解就是def worker(args)前不能有空格,确保无缩进),否则会提示不可pickle
  2. worker是在新进程中运行的,除了接受参数的过程涉及由主进程向新进程传递外,worker的body部分与主进程没有半毛钱关系,也不在主进程中运行
  3. worker传递的参数args必须是可被序列化的,这点很容易做到

一例胜千言

错误方式

class Task:
    def __init__(self,s):
        self.s = s
    def run(self):
        pass       

# 不可被pickle的对象,创建在主进程中,等待由主进程向新进程传递,这个过程一定涉及task的序列化
tasks = [Task(str(i)) for i in range(5)]

def worker(task):
    task.run()
    
if __name__ == "__main__":
    with multiprocessing.Pool() as pool:
        # 因为tasks中的元素(具体的task)都是不可被pickle的,所以由主进程向新进程传递时,就会报错can't pickle
        pool.map(worker, tasks)    

正确代码

class Task:
    def __init__(self,s):
        self.s = s
    def run(self):
        pass       

# 这里的s_list中的每一个元素,都是Python内置支持的类型,所以一定可被序列化    
s_list = [str(i) for i in range(5)]

def worker(s):
    # 重要区别,创建对象的过程是在worker中完成,而不是在主进程中
    task = Task(s)
    task.run()
    
if __name__ == "__main__":
    with multiprocessing.Pool() as pool:
        # 因为s_list中的元素(具体的s)都是可被pickle的,所以由主进程向新进程传递时,就不会报错can't pickle
        pool.map(worker, s_list) 

你可能感兴趣的:(Python,python,开发语言)