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内置函数。如果您要求工作人员计算您在笔记本中定义的函数,则将失败(正常情况除外)。

事实证明,从原则上讲,您可以进一步进行黑客攻击,并使用一些手动的代码腌制方法将其功能发送给工人。你可以找到这样一个黑客的一个很酷的例子在这里