py---池(线程池、进程池)

概念

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做参数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值