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 中使用 multiprocessing
。 IPython 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。
我正在尝试 运行 在网络上找到的 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 中使用 multiprocessing
。 IPython 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。