Python 多处理池卡住了

Python multiprocessing pool stuck

我正在尝试 运行 在网络上找到的 python 的 multiprocessing.pool 模块的一些示例代码。代码是:

def square(x):
    return x * x
if __name__ == '__main__':
    pool = Pool(processes=4)
    inputs = [0, 1, 2, 3, 4]
    outputs = pool.map(square, inputs)

但是当我尝试 运行 它时,它从未完成执行,我不得不重新启动我的 IpythonNotebook 笔记本的内核。 有什么问题?

正如您可能从 John 在评论中指出的 the answer 中读到的那样,multiprocessing.Pool 通常不应期望在交互式解释器中运行良好。要理解为什么会这样,请考虑 Pool 是如何工作的:

  • 它分叉 python 个工作人员,将当前 Python 文件的名称传递给他们。
  • 然后 worker 基本上会做 import <this file>,并听取来自 master 的消息。
  • master 通过 pickling 将函数名称连同函数参数发送给 worker。请注意,函数本身 无法发送,因为 pickle 协议不允许这样做。

当您尝试从交互式提示执行此过程时,没有合理的 "current Python file" 传递给 children 进行导入。此外,您在交互式提示中定义的函数不是任何模块的一部分(它们是动态定义的),因此 children 无法从该不存在的模块中导入。所以你最简单的选择就是避免在 IPython 中使用 multiprocessingIPython parallel 反正好多了:)


为了完整起见,我还检查了 IPython 4 运行 在 Python 2.7 下 Windows 8 的特定情况下到底发生了什么(我可以观察口译员也卡住了)。有趣的是,IPython 卡在第一位的原因并不是上面提到的原因之一。

事实证明,multiprocessing 检查是否定义了__main__.__file__,如果没有定义,则将sys.argv[0] 作为"current filename" 发送到children。对于(我的版本)IPython sys.argv[0] 等于 C:\Dev\Anaconda\lib\site-packages\ipykernel\__main__.py

不幸的是,工作进程在启动前恰好检查他们要导入的文件是否已经在他们的 sys.modules 中。 multiprocessing/forking.py 的第 488 行说:

assert main_name not in sys.modules, main_name

main_name__main__ 时(与 ipython 的工人一样)此断言失败并且工人无法启动。但是,相同的代码 "smart" 足以检查传递的名称是否为 ipython,在这种情况下,它不会进行此类检查,也不会导入任何内容。

因此,可以使用将 __main__.__file__ 定义为等于 ipython 的丑陋技巧来解决 worker 无法启动的问题。以下代码在 IPython 单元格中运行良好:

import sys
sys.modules['__main__'].__file__ = 'ipython'
from multiprocessing import Pool

pool = Pool(processes=4)
inputs = [0, 1, 2, 3, 4]
outputs = pool.map(abs, inputs)

请注意,此示例要求工作人员计算 abs,一个 built-in 函数。如果您要求工作人员计算您在笔记本中定义的函数,它将失败(优雅地,有一个例外)。

事实证明,原则上您可以更进一步地进行黑客攻击,并通过对代码进行一些手动 pickling 将您的函数发送给工作人员。您可以找到此类 hack 的一个非常酷的示例 here