python的多处理和concurrent.futures有什么区别?

时间:2014-07-22 19:32:11

标签: python multiprocessing concurrent.futures

在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之间有什么区别?什么时候优先于另一个?

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

2 个答案:

答案 0 :(得分:19)

你实际上也应该使用if __name__ == "__main__"后卫与ProcessPoolExecutormultiprocessing.Process使用Pool填充其multiprocessing.Pool,就像{ProcessPoolExecutor一样1}}确实如此,因此关于可挑选性(特别是在Windows上)等所有相同的警告都适用。

根据this statement made by Jesse Noller(Python核心贡献者),当我被问及为什么Python同时拥有两个API时,我认为multiprocessing.Pool最终会取代ProcessPoolExecutor

  

Brian和我需要处理我们打算(ed)发生的整合   因为人们对API感到满意。我最终的目标是删除   除了基本的multiprocessing.Process / Queue以外的东西   并入。*并支持线程后端。

目前,multiprocessing.Pool使用更简单(且更有限)的API与ProcessPoolExecutor执行完全相同的操作。如果您可以使用multiprocessing,请使用它,因为我认为从长远来看,它更有可能获得增强功能。

请注意,您可以使用ProcessPoolExecutorLock的所有帮助,例如QueueManagermultiprocessing.Pool等。使用的主要原因initializer如果您需要initargs / maxtasksperchild(尽管有open bug可以将这些添加到ProcessPoolExecutor中),或concurrent.futures。或者您正在运行Python 2.7或更早版本,并且不想安装(或要求您的用户安装)multiprocessing.Pool.map的后端。

修改

另外值得注意的是:根据this questionProcessPoolExecutor.map优于map。请注意,每个工作项的性能差异非常小,因此如果您在非常大的迭代中使用multiprocessing.Pool,您可能只会注意到性能差异很大。性能差异的原因是ProcessPoolExecutor将批量传递的迭代映射到块,然后将块传递给工作进程,这减少了父和子之间IPC的开销。 chunksize总是将一个项目从迭代中一次传递给子项,由于IPC开销的增加,这会导致大型迭代的性能下降得更慢。好消息是这个问题将在Python 3.5中得到修复,因为ProcessPoolExecutor.map关键字参数已被添加到{{1}},如果你知道你的话,它可用于指定更大的块大小处理大型迭代。有关详细信息,请参阅此bug

答案 1 :(得分:3)

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

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

with ProcessPoolExecutor() as executor:
    result = executor.map(calculate, range(4))
无论是从命令提示符调用还是从shell导入,都会执行

块。