python - 为什么 Windows 上的 ProcessPoolExecutor 在从另一个模块提交函数时需要 __main__ 守卫?

标签 python python-multiprocessing python-module

假设我有一个程序

import othermodule, concurrent.futures
pool = concurrent.futures.ProcessPoolExecutor()

然后我想说

fut = pool.submit(othermodule.foo, 5)
print(fut.result())

官方文档说我需要用 if __name__ == '__main__' 来保护后两个语句。这并不难,我只是想知道为什么foo 存在于 othermodule 中,它知道 (foo.__module__ == 'othermodule')。 5 是字面量 int。两者都可以在不引用创建池的模块的情况下被 pickle 和 unpickled。我看不出为什么 ProcessPoolExecutor 必须在另一边导入它。

我的模型是这样的:你启动另一个 python 进程,pickle othermodule.foo5,然后通过一些 IPC 方法(Queue、Pipe 等)发送它们 pickle .另一个进程解开它们(当然是导入 othermodule,以找到 foo 的代码),然后调用 foo(5),发送结果返回(再次通过 pickle 和一些 IPC)。显然我的模型是错误的,但我想知道它错在哪里。

这可能是唯一的原因,在 Unix 上这是通过 fork __main__ 来解决的,所以在 Windows 上( fork 实际上不起作用)他们做了最接近的过程模仿,而不是最接近的模仿意图?在这种情况下,它可以在 Windows 上修复吗?

(是的,我知道 Why does Python's multiprocessing module import __main__ when starting a new process on Windows?。在我看来,它回答了一个稍微不同的问题。但是你可以尝试使用它的答案来向我解释为什么这个问题的答案必须相同。)

最佳答案

老问题,但现在有一个很好的答案。

Is maybe the only reason, that on Unix this is solved by forking __main__, so on Windows (where fork doesn't really work) they did the closest imitation of the procedure, instead of the closest imitation of the intent? In this case, could it be fixed on Windows?

我猜测编写多处理库的人是在假设 os.fork() 可用的情况下编写的。在 Windows 上,这是以一种 hacky 方式绕过的。事实上,直到今天(2022 年),python 的内置并行化模块 multiprocessingconcurrent.futures 仍然很难使用,主要是因为 pickling 错误。当解释器可以 fork 时,这些模块工作得最好,而且不需要 pickle 任何东西。

它可以在 Windows 上修复。有一个名为 loky 的包,您可以使用 pip 或 conda 安装它。它使用名为 cloudpickle 的改进 pickling 模块(也必须安装)来调用主模块中的函数。因此,子进程不需要导入主模块,因此不必使用 if __name__ == '__main__' 守卫。 (链接到 lokydoc 的 github 存储库)您还可以尝试更高级别的包 joblib,它使用 loky 作为其后端并且也具有相同的行为。

Loky 也有一个使用可重用执行器的工具(即一个进程池,可以一次又一次地重用以减少产生的开销)。您可以查看 here 的示例. Loky 本质上使用了 concurrent.futuresProcessPoolExecutor 的语义,但底层有不同的实现。

我将给出一个带有单个执行器的示例(不可重用)。

import os

def f(x):
  return os.getpid()

with loky.ProcessPoolExecutor(max_workers=3) as pool:
    jobs = [pool.submit(f,i) for i in range(3)]  # returns Future() instances
    results = [job.result() for job in jobs]  # get results from Future

print(results)

在 Windows 上运行它,它应该会毫无障碍地返回子进程的 PID,并且不需要使用 if __name__=='__main__'

关于python - 为什么 Windows 上的 ProcessPoolExecutor 在从另一个模块提交函数时需要 __main__ 守卫?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38801229/

相关文章:

python - 如何在并行的情况下同时处理 IO 和 CPU 限制?

python脚本卡在带有多处理代码的导入模块上

python - 如何从另一个目录导入 python 包?

python - Django 打印用户列表

php - 将 Python 应用程序集成到 PHP 站点

python - 通过 Python Multiprocessing Process 子类跨作业持久化只读数据

python - Intellij/Pycharm 无法调试 Python 模块

python模块层次结构命名约定

python - 让电子邮件的收件人回复不同的地址

python - BeautifulSoup HTTPResponse 没有属性编码