在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系统上则没有必要。
答案 0 :(得分:19)
你实际上也应该使用if __name__ == "__main__"
后卫与ProcessPoolExecutor
:multiprocessing.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
,请使用它,因为我认为从长远来看,它更有可能获得增强功能。
请注意,您可以使用ProcessPoolExecutor
与Lock
的所有帮助,例如Queue
,Manager
,multiprocessing.Pool
等。使用的主要原因initializer
如果您需要initargs
/ maxtasksperchild
(尽管有open bug可以将这些添加到ProcessPoolExecutor中),或concurrent.futures
。或者您正在运行Python 2.7或更早版本,并且不想安装(或要求您的用户安装)multiprocessing.Pool.map
的后端。
修改强>
另外值得注意的是:根据this question,ProcessPoolExecutor.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导入,都会执行块。