关于python的进程池对象的一些方法的说明

python提供的multiprocessing模块可以方便的利用多进程对任务进行加速。其中,multiprocessing.Pool可以生成一个包含指定数量进程的进程池,来执行任务。要向Pool对象中提交任务,有许多方法可供选择;也正是因为如此,它们的区别以及适用场景可能会让人觉得困惑。

我在Stack Overflow上发现了一个有助于帮助理解这一点的回答,因此翻译过来。

关于imap(imap_unordered)map(map_async),主要有两点不同:

  1. 处理用户传入的可迭代对象的方式
  2. 返回计算结果的方式

1、处理用户传入的可迭代对象的方式

map方法首先将传入的可迭代对象转化为列表(假设它们之前并非列表),然后将列表分块,最后将这些数据块传入进程池中的各个进程。将可迭代对象转化为列表并分块要比逐个将可迭代对象中的元素传入各个进程更高效——特别是当可迭代对象很大时。然而,由于必须将整个列表都存入内存才能进行分块操作,因此这种方法具有很高的内存消耗。

imap方法并不会将可迭代对象转化为列表,也不会进行分块(默认情况下)。它会逐个遍历可迭代对象,每次获取一个值,然后将其传递给工作进程。因此,不必有内存溢出的担忧,但同时,对于较大的可迭代对象,其工作效率会降低(逐个取值放入进程,意味着操作系统需要不断的切换进程——这需要开销)。不过,你可以通过设置chunksize参数为较大的整数(其默认值为1)来缓解这个问题。

2、返回计算结果的方式

对于imap(imap_unordered),你可以在各个工作进程完成各自的计算时就拿到其计算结果,而不必等到所有的进程都完成。

使用map_async时,会立即返回一个AsyncResult对象,但一直要等到所有的工作进程完成它们的计算时,你才能从AsyncResult对象中拿到计算结果(从AsyncResult对象中拿到的结果与使用map方法得到的结果相同,是一个列表;map方法实际上就是调用了map_async(...).get())。所以,你绝无可能得到部分结果——要么得到全部结果,要么什么也得不到。

imapimap_unordered都会立即返回一个可迭代对象,其区别是:使用imap时,一旦进程计算完成,结果就会从可迭代对象中产生,同时仍然保持输入的可迭代对象的顺序;使用imap_unordered时,结果输出方式与imap相同,只是结果的顺序与输入无法保持一致。

下面用具体的例子来说明一下。

import multiprocessing
import time

def func(x):
    time.sleep(x)
    return x + 2

if __name__ == "__main__":    
    p = multiprocessing.Pool()
    start = time.time()
    for x in p.imap(func, [1,5,3]):
        print("{} (Time elapsed: {}s)".format(x, int(time.time() - start)))

输出如下:

3 (Time elapsed: 1s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)

解释一下为何出现这样的情况:

首先,由于选择了默认参数,进程池对象p中的进程数量为CPU的核数,这里是8。然后通过imap方法,向各个工作进程分派了任务,由于参数列表中只有3个元素,因此,一共只需要调用三个工作进程即可,且三个进程是同时处理任务的。

进程1,睡眠1秒,返回3;进程2,睡眠5秒,返回7;进程3,睡眠3秒,返回5。但因为imap方法的返回值与传入的参数同序,因此,控制台先打印进程1的结果,在打印进程2的结果是,会发现进程3的结果马上就打印出来,这是因为进程3的任务早已先于进程2完成,只不过由于需要按照顺序返回,所以必须在进程2的结果被获取之后才打印。

类似的,如果采用imap_unordered,那么,打印的结果如下:

3 (Time elapsed: 1s)
5 (Time elapsed: 3s)
7 (Time elapsed: 5s)

由于没有了顺序的限制,因此,哪个进程先执行完,哪个的结果先输出。

如果采用mapmap_async().get(),那么结果如下:

3 (Time elapsed: 5s)
7 (Time elapsed: 5s)
5 (Time elapsed: 5s)

由于必须是同时输出结果,因此,所有的时间间隔都是5s。

因此,当以下条件满足时,优先选用imap/imap_unordered而不是map/map_async

  • 作为参数的可迭代对象过大,以至于将其转为列表并分块有内存溢出的风险;
  • 你希望在所有的结果计算完成前处理那些已经完成的结果。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值