21

在 python 中实现多处理的一种简单方法是

from multiprocessing import Pool

def calculate(number):
    return number

if __name__ == '__main__':
    pool = Pool()
    result = pool.map(calculate, range(4))

基于期货的替代实现是

from concurrent.futures import ProcessPoolExecutor

def calculate(number):
    return number

with ProcessPoolExecutor() as executor:
    result = executor.map(calculate, range(4))

两种选择基本上做同样的事情,但一个显着的区别是我们不必用通常的if __name__ == '__main__'子句来保护代码。这是因为期货的实施解决了这个问题还是我们有不同的原因?

multiprocessing更广泛地说,和之间有什么区别concurrent.futures?什么时候优先于另一个?

编辑:我最初的假设是,守卫if __name__ == '__main__'只对多处理是必需的,这是错误的。显然,Windows 上的两种实现都需要这种保护,而在 unix 系统上则不需要。

4

2 回答 2

25

实际上,您也应该将if __name__ == "__main__"防护与 一起使用ProcessPoolExecutor:它multiprocessing.Process用于在幕后填充它Pool,就像这样multiprocessing.Pool做一样,因此有关可腌制性(尤其是在 Windows 上)等方面的所有相同警告都适用。

根据Jesse Noller(Python 核心贡献者)在被问及为什么 Python 有这两种 API 时,我相信这ProcessPoolExecutor意味着最终会取代.multiprocessing.Pool

当人们对 API 感到满意时,Brian 和我需要着手进行我们打算进行的整合。我的最终目标是从 MP 中删除除基本的 multiprocessing.Process/Queue 之外的任何内容并进入并发。* 并支持它的线程后端。

目前,大多数情况下都在做与使用更简单(且更有限)的 APIProcessPoolExecutor完全相同的事情。multiprocessing.Pool如果您可以摆脱 using ProcessPoolExecutor,请使用它,因为我认为从长远来看它更有可能获得增强。请注意,您可以使用multiprocessingwith ProcessPoolExecutor、like LockQueueManager等中的所有帮助程序,因此需要这些不是使用 的理由multiprocessing.Pool

不过,它们的 API 和行为存在一些显着差异:

  1. 如果 a 中的 ProcessProcessPoolExecutor突然终止,则会引发BrokenProcessPool异常,中止任何等待池执行工作的调用,并阻止提交新工作。如果同样的事情发生在 amultiprocessing.Pool上,它会默默地替换终止的进程,但在该进程中完成的工作永远不会完成,这可能会导致调用代码永远挂起,等待工作完成。

  2. 如果您运行的是 Python 3.6 或更低版本initializerinitargsProcessPoolExecutor. 仅在 3.7 中添加了对此的支持)。

  3. 没有支持ProcessPoolExecutorfor maxtasksperchild

  4. concurrent.futures在 Python 2.7 中不存在,除非您手动安装 backport。

  5. 如果您在 Python 3.5 以下运行,根据这个问题multiprocessing.Pool.map性能优于ProcessPoolExecutor.map. 请注意,每个工作项的性能差异非常小,因此如果您使用map非常大的可迭代对象,您可能只会注意到较大的性能差异。造成性能差异的原因是multiprocessing.Pool会将传递给map的iterable批量化成chunk,然后将chunk传递给worker进程,这样就减少了父子之间IPC的开销。ProcessPoolExecutor总是(或默认情况下,从 3.5 开始)一次将一个项目从可迭代对象传递给子对象,由于 IPC 开销增加,这可能导致大型可迭代对象的性能更慢。好消息是这个问题在 Python 3.5 中得到修复,因为chunksize关键字参数已添加到ProcessPoolExecutor.map,当您知道您正在处理大型迭代时,可用于指定更大的块大小。有关更多信息,请参阅此错误

于 2014-07-22T19:40:06.270 回答
3

if __name__ == '__main__':只是意味着您在命令提示符下使用python <scriptname.py> [options]而不是import <scriptname>在 python shell 中调用了脚本。

当您从命令提示符调用脚本时,将调用该__main__方法。在第二个区块中,

with ProcessPoolExecutor() as executor:
    result = executor.map(calculate, range(4))

无论是从命令提示符调用还是从 shell 导入,都会执行块。

于 2014-07-22T19:37:58.593 回答