Python多处理池卡住了
问题内容:
我正在尝试运行在网络上找到的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在评论中指出的答案中看到的那样multiprocessing.Pool
,通常不应期望它在交互式解释器中能很好地工作。要了解为什么会发生这种情况,请考虑Pool
其工作原理:
- 它将派生python worker,并将当前Python文件的名称传递给他们。
- 然后,工作人员基本上执行
import <this file>
,并侦听来自主服务器的消息。 - 主机通过酸洗将函数名称和函数参数发送给工作程序。请注意, 函数本身 无法发送,因为pickle协议不允许这样做。
当您尝试从交互式提示执行此过程时,没有合理的“当前Python文件”可传递给子项进行导入。此外,您在交互式提示中定义的功能不是任何模块的一部分(它们是动态定义的),因此子级无法从不存在的模块中导入这些功能。因此,最简单的选择就是避免multiprocessing
在IPython中使用。无论如何,IPython并行要好得多:)
为了完整起见,我还检查了在Windows 8上以Python 2.7运行的IPython
4的特殊情况下发生了什么(在这里我也可以观察到解释器卡住了)。有趣的是,IPython被卡在首位的原因并非上述原因之一。
事实证明,多重处理检查是否__main__.__file__
已定义,如果未定义,则将其sys.argv[0]
作为“当前文件名”发送给子级。在(我的版本)IPythonsys.argv[0]
等于的情况下C:\Dev\Anaconda\lib\site- packages\ipykernel\__main__.py
。
不幸的是,工作者进程在启动之前碰巧要检查要导入的文件是否已在sys.modules
。第488行multiprocessing/forking.py
说:
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
内置函数。如果您要求工作人员计算您在笔记本中定义的函数,则将失败(正常情况除外)。
事实证明,从原则上讲,您可以进一步进行黑客攻击,并使用一些手动的代码腌制方法将其功能发送给工人。你可以找到这样一个黑客的一个很酷的例子在这里。