ProcessPoolExecutor: TypeError: cannot pickle 'PyCapsule' object

ProcessPoolExecutor: TypeError: cannot pickle 'PyCapsule' object

我正面临 concurrent.futuresProcessPoolExecutor 的问题,我正试图在我的 类:

中使用它
    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() 获取结果时,我在 test() 函数中看不到 print 消息的输出。

然后,当我明确要求未来的结果时,我得到 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 中()

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

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

谢谢!

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

当您传递对象方法时,事情会变得更加复杂。子进程在其函数中找不到这样的方法。因此,父级必须对整个对象进行 pickle 和 ship。

这是您遇到问题的地方:

cls(buf, protocol).dump(obj)

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

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

关于什么容易腌制什么不容易腌制的更多信息你可以参考它的模块documentation

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