由于python内部GIL(全局解释器锁)的存在,所以python的线程实际上并不能很好的起到任务并行处理的作用,尤其是无法充分利用系统多核的优势,因此想要利用多核处理并行任务,就需要用到多进程——multiprocess。由于多进程任务时,经常需要返回函数的结果,这里主要关注进程返回值的获取。
首先定义一个简单函数:
def add(n):
s = 0
for i in range(n):
s += 1
print(f'子进程{n}')
return s
用串行的方法:
import time
lists = [10000000, 20000000, 5000000, 30000000]
results = []
start = time.time()
for n in lists:
results.append(add(n))
print(f'recurrent time cost: {time.time() - start}s')
打印结果:
子进程10000000
子进程20000000
子进程5000000
子进程30000000
recurrent time cost: 3.95 s
很明显,是按顺序执行函数的。下面用多进程实现,看速度是否会提升。
from multiprocessing import Pool
pool = Pool(processes=4) # 子进程数
results = []
start = time.time()
for n in lists:
result = pool.apply_async(add, (n,)
# apply_async是apply的并行变体,使用apply会造成队列阻塞,没法实现并行。
# 输入是函数名称(注意没有括号),以及函数的参数(以元组形式按位置排列输入)
results.append(result)
# 把返回的结果添加到results中
pool.close() # 关闭进程池,不能再添加进程
pool.join() # 主进程等待所有进程执行完毕
print(f'multi-process time cost: {time.time() - start}s')
#获取结果
for res in results:
print(res.get())
执行的结果:
子进程5000000
子进程10000000
子进程20000000
子进程30000000
multi-process time cost: 1.91 s
10000000
20000000
5000000
30000000
可以看到快的先执行完,慢的后执行完,但结果返回的顺序仍与进程添加的顺序一致。
上面的例子表明python的多进程确实可以实现多任务并行,下面测试多线程的效果,是否无法实现CPU密集型的多任务并行。同样我们使用上面的add函数作为基础运算函数。代码如下:
# 使用线程池
from concurrent.futures import ThreadPoolExecutor
pool = ThreadPoolExecutor(max_workers=4)
start = time.time()
for res in pool.map(add, lists):
# print(res)
pass
print(f'multi-tfreads time cost: {time.time() - start}s')
看一下结果
子进程5000000
子进程10000000
子进程20000000
子进程30000000
multi-tfreads time cost: 5.01 s
可以看到耗时比for循环还长,因为存在线程开启销毁等时间消耗,所以python的线程对CPU密集型任务不太行,这种情况下还是用多进程执行并行任务的好!