概念
1.什么是池
在程序开始的时候,还没提交任务,先创建几个线程或者进程放在一个池子里,这就是池
2.为什么用池
如果先开好进程/线程,那么有任务之后就可以直接使用这个池中的数据了。
开好的线程或进程会一直存在在池中,可以被多个任务反复利用,这样极大的减少可开始\关闭\调度线程/进程的时间开销。
池中的线程或进程个数控制了操作系统需要调度的任务个数,控制池中的单位有利于提高操作系统的效率,减轻操作系统的负担。
1.模块介绍
concurrent.futures模块提供了高度封装的异步调用接口。
- ThreadPoolExecutor:线程池,提供异步调用
- ProcessPoolExecutor:进程池,提供异步调用
线程池、进程池都能用相似的方式开启\使用
2.基本方法
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ProcessPoolExecutor
submit(func,*args,**kwargs)
#异步提交任务
map(func,*iterables,timeout=None,chunksize=1)
#取代for循环submit的操作
shutdown(wait=True)
#相当于进程池的pool.close()+pool.join()操作
#wait=True:等待池中的所有任务执行完毕,回收完资源后才继续
#wait=False:立即返回,不会等待池内的任务执行完毕
#但是不管wait参数为何值,整个程序都会等待所有任务执行完毕
#submit和map必须写在shutdown之前
result(timeout=None)
#取得结果
add_done_callback(func)
#回调函数
done()
#判断某一个线程\进程是否完成
cancle()
#取消某个任务
3.示例
线程池
池里开的线程数(一般会根据io的比例定制):一般用时开cpu_count*5就可以了
import time
import random
from threading import current_thread
from concurrent.futures import ThreadPoolExecutor
def func(a,b):
print(current_thread().ident,'start',a,b)
time.sleep(random.randint(1,4))
print(current_thread().ident,'end')
#不管是进程池还是线程池,都是排队上厕所原则,即如果有空线程\进程,那来了的任务就会去占用执行
tp = ThreadPoolExecutor(4)
#实例化,创建池
for i in range(20):
tp.submit(func,i,b = i+1)
#向池中提交任务,submit传参数(按照位置传、按照关键字传)
进程池
进程池里原生实现了数据通信
高计算的场景,没有io(无文件操作、无数据库操作、无网络操作、无input):池内开进程数:cpu总数的1到2倍之间,cpu_count+1即可
import random
import time
import os
from concurrent.futures import ProcessPoolExecutor
def func(a,b):
print(os.getpid(),'start',a,b)
time.sleep(random.randint(1,4))
print(os.getpid(),'end')
return a*b
#if __name__ == '__main__':#windows系统下关于进程的必须有这句
#tp = ProcessPoolExecutor(4)
#for i in range(20):
#ret = tp.submit(func,i,b = i+1)
##ret:future对象
#print(ret.result())
##取得进程执行的结果
##但是程序在此处变成了同步,顺序执行
#又想要结果,又不想让程序同步
if __name__ == '__main__':
tp = ProcessPoolExecutor(4)
future_l = []
for i in range(20):
ret = tp.submit(func,i,b = i+1)
#异步非阻塞
#不论哪个先执行完得到结果,在下一个for obj里都会按顺序打印
#例如:如果第2个先完了,而第1个未完,那么会阻塞住,等待1结果(下一个for循环)
future_l.append(ret)
for obj in future_l:
print(obj.result())
#同步阻塞
map
只适合传简单的参数,并且必须是一个可迭代的类型作为参数
import os
import time
import random
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
def func(a):
b = a+1
print(os.getpid(),'start',a,b)
time.sleep(random.randint(1,4))
print(os.getpid(),'end')
return a*b
if __name__ == '__main__':
tp = ProcessPoolExecutor(4)
ret = tp.map(func,range(20))
#ret:iterable
#map将range(20)传入func里
for key in ret:
print(key)
回调函数:add_done_callback()
效率最高的
池里的任务产生结果后,可以立即对结果进行处理,而不用按照顺序结果去处理结果
import random
import time
from threading import current_thread
from concurrent.futures import ThreadPoolExecutor
def func(a,b):
print(current_thread().ident,'start',a,b)
time.sleep(random.randint(1,4))
print(current_thread().ident,'end')
return a*b
def print_func(ret):
print(ret.result())
if __name__ == '__main__':
tp = ThreadPoolExecutor(4)
for i in range(20):
ret = tp.submit(func,i,b = i+1)
#异步非阻塞
ret.add_done_callback(print_func)
#异步阻塞
#回调函数,给ret对象绑定一个回调函数,等待ret对应的任务执行完有结果后,立即调用print_func函数
#就可以对结果进行处理,而不用严格按照顺序的结果去处理结果
以上原理类似于队列,可以想象成队列的模式,ret任务会在执行完毕的瞬间把返回值future对象发送到队列里,先进先出,立即触发print_func函数,并且把任务的返回值对象传递给print_func做参数