首页 > 解决方案 > ProcessPoolExecutor:TypeError:无法腌制“PyCapsule”对象

问题描述

我正面临concurrent.futures's的问题ProcessPoolExecutor,我正试图在我的课程中使用它:

    def __init__():
         self._pool = ProcessPoolExecutor()

    def handle_event():
         ...
         filepath: Path = xxxx
         future = self._pool.submit(self.test, filepath)
         res = future.result()
         self.logger.debug(res)

    def test(self, filepath: Path):
        print("Test ProcessPoolExecutor")
        return 3

这个小代码有一个奇怪的行为。首先,当我删除获取结果时,我在函数future.result()中看不到我的print消息输出。test()

然后,当我明确要求未来的结果时,我得到一个TypeError

Traceback (most recent call last):
  File "/usr/lib/python3.8/multiprocessing/queues.py", line 239, in _feed
    obj = _ForkingPickler.dumps(obj)
  File "/usr/lib/python3.8/multiprocessing/reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
TypeError: cannot pickle 'PyCapsule' object
  1. 我在这里不明白的是,我发送(Path)和接收(int)的类型都是 Picklable。

  2. 其次,我什至不知道这个 PyCapsule 依赖项是什么,因为它没有出现在我的requirements.txt,也没有出现在pip freeze相关的 SO 帖子)中

.nox/run/bin/pip freeze | grep -E '(capsule|dill)'

知道为什么我看不到我的打印语句出现吗?PyCapsule 错误呢?是我的类型有问题,还是应用程序中的其他地方有问题?

谢谢 !

标签: pythonmultiprocessingpickleconcurrent.futures

解决方案


在为您安排作业时,concurrent.futures.ProcessPoolExecutor会同时发送函数名称和参数。子进程接收函数名称并在其内存中查找它。

当您传递一个对象方法时,事情变得更加复杂。子进程在其函数中找不到这样的方法。因此,父母必须腌制和运送整个对象。

这是您遇到问题的地方:

cls(buf, protocol).dump(obj)

pickle 协议不知道如何序列化您的对象,因为它包含不可提取的组件。特别是,PyCapsule是一种内部 Python 数据结构。

建议不要将对象方法传递给进程池,因为很难预测对象是否可腌制。此外,您还要支付序列化整个对象并通过管道传输它而不是仅仅传送函数名称的增加成本。

有关什么容易腌制和什么不容易腌制的更多信息,您可以参考其模块文档

如果您不能遵守上述建议,您可以查看其他 pickle 实现,例如dill


推荐阅读