正如您从 John 在 cmets 中指出的 the answer 中所读到的那样,通常不应期望 multiprocessing.Pool 在交互式解释器中运行良好。要了解为什么会这样,请考虑 Pool 的工作方式:
- 它派生出 python 工作者,将当前 Python 文件的名称传递给它们。
- 然后,worker 基本上执行
import <this file>,并监听来自 master 的消息。
- master 通过 pickling 将函数名称和函数参数发送给 worker。请注意,函数本身无法发送,因为 pickle 协议不允许这样做。
当您尝试从交互式提示执行此过程时,没有合理的“当前 Python 文件”可传递给子项进行导入。此外,您在交互式提示中定义的函数不是任何模块的一部分(它们是动态定义的),因此无法由子模块从该不存在的模块中导入。所以你最简单的选择就是避免在 IPython 中使用multiprocessing。无论如何IPython parallel 好多了:)
为了完整起见,我还检查了在 Windows 8 上的 Python 2.7 下运行的 IPython 4 的特定情况下到底发生了什么(我可以观察到解释器也卡住了)。有趣的是,IPython 卡在首位的原因并不是上面提到的原因之一。
事实证明,多处理检查是否定义了__main__.__file__,如果没有定义,则将sys.argv[0] 作为“当前文件名”发送给孩子。在(我的版本)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 的worker 一样),此断言失败并且worker 无法启动。然而,相同的代码足够“智能”来检查传递的名称是否为ipython,在这种情况下,它不会进行此类检查,也不会导入任何内容。
因此,工人无法启动的问题可以通过将__main__.__file__ 定义为等于ipython 的丑陋技巧来解决。以下代码在 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,这是一个内置函数。如果您要求工作人员计算您在笔记本中定义的函数,它将失败(优雅地,有一个例外)。
事实证明,原则上,您可以在黑客攻击方面走得更远,并通过手动腌制他们的代码将您的功能发送给工作人员。你可以在here找到一个非常酷的例子。