[932]multiprocessing进程池错误:TypeError: can‘t pickle generator objects

昨天在类中使用进程池时出现了TypeError: can't pickle generator objects的错误, 这是进程池无法序列化生成器对象的错误。

我尝试使用了concurrent.futures模块下的ProcessPoolExecutor进程池方法,依然是不行的。

这里使用以下代码大致还原一下遇到的错误:

from multiprocessing import Pool


class T:
    def __init__(self, a):
        self.a = a
        self.b = (i for i in range(5))

    def add(self, n):
        for i in self.b:
            print(i + n)

    def run(self):
        p = Pool()
        p.map(self.add, self.a)


if __name__ == '__main__':
    a = [i for i in range(5)]
    s = T(a)
    s.run()

当运行上述代码时,将会出现以下的具体错误:

Traceback (most recent call last):
  File "/Users/wangyue/Python/txt_to_MongoDB/mult_process.py", line 22, in <module>
    s.run()
  File "/Users/wangyue/Python/txt_to_MongoDB/mult_process.py", line 15, in run
    p.map(self.add, self.a)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 268, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 657, in get
    raise self._value
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/pool.py", line 431, in _handle_tasks
    put(task)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/connection.py", line 206, in send
    self._send_bytes(_ForkingPickler.dumps(obj))
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/multiprocessing/reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
TypeError: can't pickle generator objects

问题分析

process多进程的过程涉及pickle,俗称序列化,多进程传参数会自动将参数序列化,pickle.dumps()
在开启多进程传参过程中传递了不可序列化的对象,导致报错TypeError: can't pickle generator objects

在多进程传参时要避免雷区,就首先要检测自己的参数能不能序列化,在传参前检测一下 pickle.dumps() 是否会报错

问题的直接原因是我在__init__构造函数内定义了一个生成器对象,不管你最终多进程的实例方法是否有用到该生成器对象,都会导致报错,因为它在构造函数内:

    def __init__(self, a):
        self.a = a
        # 这是一个生成器对象
        self.b = (i for i in range(5)

由于我使用进程池调用的方法是一个类对象中中的实例方法,而进程池无法pickle序列化该self对象,造成错误。

解决方案

若要对类实例方法调用进程池,则不能在_init_中定义生成器对象,你可以转换为一个列表。
若一定需要一个生成器对象,则可以在_init_之外使用@property定义一个属性,其余代码也均不需要做修改:

from multiprocessing import Pool


class T:
    def __init__(self, a):
        self.a = a
        # 修改方式1:将对象转换为列表
        # self.b = [i for i in range(5)]

    # 修改方式2:定义属性方法获取生成器对象
    @property
    def b(self):
        return (i for i in range(5))

    def add(self, n):
        for i in self.b:
            print(i + n)

    def run(self):
        p = Pool()
        p.map(self.add, self.a)


if __name__ == '__main__':
    a = [i for i in range(5)]
    s = T(a)
    s.run()

修改后的多进程代码可以正常运行了。

其余修改方式:

貌似可以使用dill模块实现pickle模块无法序列化的一些对象,不知道是否包括生成器对象。不过目前没做研究,有兴趣的小伙伴可以再研究研究。

来源:https://www.jianshu.com/p/54ae043d4868
https://blog.csdn.net/qq_27158747/article/details/98948548

当尝试将`map`对象作为参数传递给多进程函数时,会引发`TypeError: can't pickle select.epoll objects`错误。这是因为`map`对象无法序列化,而多进程在启动时需要对参数进行序列化。 要解决这个问题,可以使用`multiprocessing`模块的`Manager`类来创建一个可序列化的代理对象来传递`map`对象。以下是一个示例代码: ```python import multiprocessing def process_function(my_map): # 在这里使用my_map对象 for item in my_map: # ... if __name__ == '__main__': my_map = [1, 2, 3, 4, 5] # 这里是你的map对象 with multiprocessing.Manager() as manager: map_proxy = manager.list(my_map) process = multiprocessing.Process(target=process_function, args=(map_proxy,)) process.start() process.join() ``` 在上述示例代码中,我们首先创建了一个`map`对象`my_map`,然后使用`Manager`类创建了一个可序列化的代理对象`map_proxy`。这个代理对象可以在多进程间共享,并且支持对其进行迭代操作。 接下来,我们创建了一个多进程,并将代理对象`map_proxy`作为参数传递给进程函数。在进程函数中,我们可以直接使用这个代理对象进行操作,例如迭代其中的元素。 需要注意的是,代理对象会在多进程间进行数据同步,所以对于大型的`map`对象,可能会有一些性能上的开销。另外,还可以考虑使用其他的进程间通信机制,如`Queue`、`Pipe`等,具体取决于你的需求和代码结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值