python的多进程和concurrent.futures有什么区别?

问题描述 投票:17回答:2

在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__' 条款。这是因为期货的实施照顾到了这一点,还是我们有其他原因?

更广义的说,这两者之间的区别是什么?multiprocessingconcurrent.futures? 什么时候选择一种比另一种更合适?

EDIT:我最初的假设是,守 if __name__ == '__main__' 只有在多处理时才需要,这是错的。很显然,在windows系统上两种实现方式都需要这个防护,而在unix系统上则不需要。

python multiprocessing concurrent.futures
2个回答
22
投票

实际上你应该使用 if __name__ == "__main__" 侍卫 ProcessPoolExecutor,也是。它使用的是 multiprocessing.Process 以填充其 Pool 掩耳盗铃 multiprocessing.Pool 所以所有关于可拾取性(特别是在Windows上)等的注意事项都是一样的。

我相信 ProcessPoolExecutor 是为了最终取代 multiprocessing.Pool根据 Jesse Noller的发言 (Python 核心贡献者),当被问及为什么 Python 有两个 API 时。

Brian和我需要在人们对API感到满意的时候,对我们打算进行的整合进行工作。我的最终目标是将除了基本的多处理.ProcessQueue之外的任何东西从MP中移除,并将其放入concurrent.*中,并为其支持线程后端。

目前来说。ProcessPoolExecutor 主要是在做与之完全相同的事情 multiprocessing.Pool 用一个更简单(更有限)的API。如果你能摆脱使用 ProcessPoolExecutor,使用这个,因为我认为它更有可能在长期得到增强。请注意,你可以使用所有的助手,从 multiprocessingProcessPoolExecutor譬如 Lock, Queue, Manager等,所以需要这些并不是使用的理由。multiprocessing.Pool.

但它们的API和行为有一些显著的不同。

  1. 如果一个进程在 ProcessPoolExecutor 戛然而止。a BrokenProcessPool 异常,中止任何等待池进行工作的调用,并阻止新工作的提交。如果同样的事情发生在一个 multiprocessing.Pool 它将默默地替换终止的进程,但该进程中正在进行的工作将永远不会完成,这很可能导致调用代码永远挂起等待工作完成。

  2. 如果你运行的是Python 3.6或更低版本,支持的是 initializerinitargs 缺少 ProcessPoolExecutor. 这方面的支持在3.7中才被加入。).

  3. 3.7中没有支持。ProcessPoolExecutor 对于 maxtasksperchild.

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

  5. 如果您运行的是 Python 3.5 以下的版本,根据 这个问题, multiprocessing.Pool.map 胜过 ProcessPoolExecutor.map. 请注意,性能差异非常小 每个工作项目因此,你可能只会在你使用 map 在一个非常大的iterable上。性能差异的原因是 multiprocessing.Pool 会将传递过来的iterable批量映射成chunks,然后将chunks传递给worker进程,这样可以减少父进程和子进程之间的IPC开销。ProcessPoolExecutor always (或者从 3.5 开始的默认情况下) 每次只从 iterable 中传递一个项目给子进程,由于增加了 IPC 开销,这可能会导致大的 iterable 性能更慢。好消息是这个问题在Python 3.5中得到了解决,因为Python 3.5中的 chunksize 关键字参数已被添加到 ProcessPoolExecutor.map当你知道你要处理大的迭代物时,可以用它来指定一个更大的分块大小。 请看这个 虫子 获取更多信息。


3
投票

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

当你在命令提示符下调用脚本时,会在 __main__ 方法被调用。在第二个块中,

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

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

© www.soinside.com 2019 - 2024. All rights reserved.