Python 3.8 multiprocessing: TypeError: cannot pickle 'weakref' object

Python 3.8 multiprocessing: TypeError: cannot pickle 'weakref' object

我们正在尝试使用 multiprocessing 模块执行这段代码:

import multiprocessing as mp

ctx = mp.get_context("spawn")
(child, pipe) = ctx.Pipe(duplex=True)
job_process = ctx.Process(
    name="my_job",
    target=job_func,
    args=(
        child,
        server_info,
        manager,
        job_config,
        config_file,
    ),
)
job_process.start()

并且我们 运行 在 Python 3.8 中出现了以下错误,在 Python 3.6 中没有看到:

 Traceback (most recent call last):
   File "path/to/venv/lib/python3.8/site-packages/path/to/file.py", line 137, in includeme 
     job_process.start()
   File "/usr/lib/python3.8/multiprocessing/process.py", line 121, in start
     self._popen = self._Popen(self)
   File "/usr/lib/python3.8/multiprocessing/context.py", line 284, in _Popen
     return Popen(process_obj)
   File "/usr/lib/python3.8/multiprocessing/popen_spawn_posix.py", line 32, in __init__
     super().__init__(process_obj)
   File "/usr/lib/python3.8/multiprocessing/popen_fork.py", line 19, in __init__
     self._launch(process_obj)
   File "/usr/lib/python3.8/multiprocessing/popen_spawn_posix.py", line 47, in _launch
     reduction.dump(process_obj, fp)
   File "/usr/lib/python3.8/multiprocessing/reduction.py", line 63, in dump
     ForkingPickler(file, protocol).dump(obj)
 TypeError: cannot pickle 'weakref' object

进程的启动方式或提供的参数是否需要更改或检查?任何方向表示赞赏!

这个问题可能是您应用程序中一个更大错误的 side-effect。 You must use import guards when using multiprocessing in 'spawn' mode,不这样做会发生一些奇怪的事情。特别是:

  1. 在 spawn 模式下完成的 fork-emulation 将尝试序列化一些主模块的状态,以便传输到 child 进程以类似地初始化 child; multiprocessing.Process itself is not picklable as of Python 3.7(虽然有一个待修复的补丁),所以你绝对不想把它放在任何可能被腌制的地方。

  2. 通过不使用守卫,child 进程对主模块的导入完成 所有 主模块所做的事情,包括启动一个child(它本身会启动 child,依此类推,无穷无尽)。

这两个问题都应该通过使用 import guard 并将所有内容放入 guard 中调用的 main 函数来解决:

import multiprocessing as mp

# Wrapping in function avoids implicit dependencies on mutable global state
def main():
    ctx = mp.get_context("spawn")
    (child, pipe) = ctx.Pipe(duplex=True)
    job_process = ctx.Process(
        name="my_job",
        target=job_func,
        args=(
            child,
            server_info,
            manager,
            job_config,
            config_file,
        ),
    )
    job_process.start()

# Guard ensures only the parent process tries to actually run main()
if __name__ == '__main__':
    main()