python ThreadPoolExecutor线程池(实例)
初识
Python
中已经有了threading
模块,为什么还需要线程池呢,线程池又是什么东西呢?在介绍线程同步的信号量机制的时候,举得例子是爬虫的例子,需要控制同时爬取的线程数,例子中创建了20个线程,而同时只允许3个线程在运行,但是20个线程都需要创建和销毁,线程的创建是需要消耗系统资源的,有没有更好的方案呢?其实只需要三个线程就行了,每个线程各分配一个任务,剩下的任务排队等待,当某个线程完成了任务的时候,排队任务就可以安排给这个线程继续执行。
这就是线程池的思想(当然没这么简单),但是自己编写线程池很难写的比较完美,还需要考虑复杂情况下的线程同步,很容易发生死锁。从Python3.2
开始,标准库为我们提供了concurrent.futures
模块,它提供了ThreadPoolExecutor
和ProcessPoolExecutor
两个类,实现了对threading
和multiprocessing
的进一步抽象(这里主要关注线程池),不仅可以帮我们自动调度线程,还可以做到:
- 主线程可以获取某一个线程(或者任务的)的状态,以及返回值。
- 当一个线程完成的时候,主线程能够立即知道。
- 让多线程和多进程的编码接口一致。
实例
简单使用
from concurrent.futures import ThreadPoolExecutor
import time# 参数times用来模拟网络请求的时间
def get_html(times):time.sleep(times)print("get page {}s finished".format(times))return timesexecutor = ThreadPoolExecutor(max_workers=2)
# 通过submit函数提交执行的函数到线程池中,submit函数立即返回,不阻塞
task1 = executor.submit(get_html, (3))
task2 = executor.submit(get_html, (2))
# done方法用于判定某个任务是否完成
print(task1.done())
# cancel方法用于取消某个任务,该任务没有放入线程池中才能取消成功
print(task2.cancel())
time.sleep(4)
print(task1.done())
# result方法可以获取task的执行结果
print(task1.result())# 执行结果
# False # 表明task1未执行完成
# False # 表明task2取消失败,因为已经放入了线程池中
# get page 2s finished
# get page 3s finished
# True # 由于在get page 3s finished之后才打印,所以此时task1必然完成了
# 3 # 得到task1的任务返回值
ThreadPoolExecutor
构造实例的时候,传入max_workers
参数来设置线程池中最多能同时运行的线程数目。- 使用
submit
函数来提交线程需要执行的任务(函数名和参数)到线程池中,并返回该任务的句柄(类似于文件、画图),注意submit()
不是阻塞的,而是立即返回。 - 通过
submit
函数返回的任务句柄,能够使用done()
方法判断该任务是否结束。上面的例子可以看出,由于任务有2s的延时,在task1
提交后立刻判断,task1
还未完成,而在延时4s之后判断,task1
就完成了。 - 使用
cancel()
方法可以取消提交的任务,如果任务已经在线程池中运行了,就取消不了。这个例子中,线程池的大小设置为2,任务已经在运行了,所以取消失败。如果改变线程池的大小为1,那么先提交的是task1
,task2
还在排队等候,这是时候就可以成功取消。 - 使用
result()
方法可以获取任务的返回值。查看内部代码,发现这个方法是阻塞的。
as_completed
上面虽然提供了判断任务是否结束的方法,但是不能在主线程中一直判断啊。有时候我们是得知某个任务结束了,就去获取结果,而不是一直判断每个任务有没有结束。这是就可以使用as_completed
方法一次取出所有任务的结果。
from concurrent.futures import ThreadPoolExecutor, as_completed
import time# 参数times用来模拟网络请求的时间
def get_html(times):time.sleep(times)print("get page {}s finished".format(times))return timesexecutor = ThreadPoolExecutor(max_workers=2)
urls = [3, 2, 4] # 并不是真的url
all_task = [executor.submit(get_html, (url)) for url in urls]for future in as_completed(all_task):data = future.result()print("in main: get page {}s success".format(data))# 执行结果
# get page 2s finished
# in main: get page 2s success
# get page 3s finished
# in main: get page 3s success
# get page 4s finished
# in main: get page 4s success
as_completed()
方法是一个生成器,在没有任务完成的时候,会阻塞,在有某个任务完成的时候,会yield
这个任务,就能执行for循环下面的语句,然后继续阻塞住,循环到所有的任务结束。从结果也可以看出,先完成的任务会先通知主线程。
map
除了上面的as_completed
方法,还可以使用executor.map
方法,但是有一点不同。
from concurrent.futures import ThreadPoolExecutor
import time# 参数times用来模拟网络请求的时间
def get_html(times):time.sleep(times)print("get page {}s finished".format(times))return timesexecutor = ThreadPoolExecutor(max_workers=2)
urls = [3, 2, 4] # 并不是真的urlfor data in executor.map(get_html, urls):print("in main: get page {}s success".format(data))
# 执行结果
# get page 2s finished
# get page 3s finished
# in main: get page 3s success
# in main: get page 2s success
# get page 4s finished
# in main: get page 4s success
使用map
方法,无需提前使用submit
方法,map
方法与python
标准库中的map
含义相同,都是将序列中的每个元素都执行同一个函数。上面的代码就是对urls
的每个元素都执行get_html
函数,并分配各线程池。可以看到执行结果与上面的as_completed
方法的结果不同,输出顺序和urls
列表的顺序相同,就算2s的任务先执行完成,也会先打印出3s的任务先完成,再打印2s的任务完成。
wait
wait
方法可以让主线程阻塞,直到满足设定的要求。
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETED
import time# 参数times用来模拟网络请求的时间
def get_html(times):time.sleep(times)print("get page {}s finished".format(times))return timesexecutor = ThreadPoolExecutor(max_workers=2)
urls = [3, 2, 4] # 并不是真的url
all_task = [executor.submit(get_html, (url)) for url in urls]
wait(all_task, return_when=ALL_COMPLETED)
print("main")
# 执行结果
# get page 2s finished
# get page 3s finished
# get page 4s finished
# main
wait
方法接收3个参数,等待的任务序列、超时时间以及等待条件。等待条件return_when
默认为ALL_COMPLETED
,表明要等待所有的任务都结束。可以看到运行结果中,确实是所有任务都完成了,主线程才打印出main
。等待条件还可以设置为FIRST_COMPLETED
,表示第一个任务完成就停止等待。
项目实例
from concurrent.futures import ThreadPoolExecutor, wait, ALL_COMPLETED, FIRST_COMPLETED
import time# 参数times用来模拟网络请求的时间
def get_html(times):time.sleep(times)print("get page {}s finished".format(times))return timestask_pool = ThreadPoolExecutor(10)
request_list=[]
for next_ip in range (ip_start,ip_stop,256): if (next_ip+256)>ip_stop:breakrequest_list.append(task_pool.submit(self.Thread_ips,next_ip))
wait(request_list, return_when=ALL_COMPLETED)
总结
- future的设计理念很棒,在线程池/进程池和携程中都存在future对象,是异步编程的核心。
- ThreadPoolExecutor 让线程的使用更加方便,减小了线程创建/销毁的资源损耗,无需考虑线程间的复杂同步,方便主线程与子线程的交互。
- 线程池的抽象程度很高,多线程和多进程的编码接口一致。
python ThreadPoolExecutor线程池(实例)相关推荐
- Python ThreadPoolExecutor线程池
概念 Python中已经有了threading模块,为什么还需要线程池呢,线程池又是什么东西呢?以爬虫为例,需要控制同时爬取的线程数,例子中创建了20个线程,而同时只允许3个线程在运行,但是20个线程 ...
- Python运用线程池ThreadPoolExecutor多线程下载搜索图片
用chrome解析某搜索网站图片搜索请求包,再参考网上一些已有的代码,在vscode+python3.7环境,多线程下载搜索结果页面上图片的代码,其中加入了键盘中止和ThreadPoolExecuto ...
- async spring 默认线程池_Spring boot注解@Async线程池实例详解
这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从Spring3开始提供了@A ...
- 13.ThreadPoolExecutor线程池之submit方法
jdk1.7.0_79 在上一篇<ThreadPoolExecutor线程池原理及其execute方法>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法 ...
- Java多线程学习总结(4)——ThreadPoolExecutor 线程池的拒绝策略学习总结
前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...
- ThreadPoolExecutor线程池原理
ThreadPoolExecutor线程池原理 线程池原理 1. 线程池的简单介绍 1.1 线程池是什么 1.2 线程池解决的核心问题是什么 2. 线程池的实现原理 2.1 线程池的执行流程 2.2 ...
- Python实现线程池
Python实现线程池 最近在做一些文本处理方面的事情,考虑到程序利用并发性可以提高执行效率(不纠结特殊反例),于是入围的Idea如使用多进程或多线程达到期望的目标,对于进程或线程的创建是有代价的,那 ...
- ThreadPoolExecutor线程池,shutdown和shutdownNow关闭线程池方式对比,以及确保线程池能够彻底关闭的一种方式
1. ThreadPoolExecutor线程池 1.1 创建线程池,构造方法的几个参数说明及创建如下. 1.2 shutdown方式关闭线程池 a. 空闲且能interrupt表示该线程处于阻塞等待 ...
- Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】
基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...
最新文章
- 启明云端分享| ESP32-C3智能写字板应用解决方案
- Kubernetes安装之十:配置node节点之kube-proxy
- C语言中如何求一天是星期几,计算任何一天是星期几的C语言源代码.
- Spring Boot Admin –用于管理Spring Boot应用程序的Admin UI
- mysql profile 调试sql_SQL Server profile使用技巧
- 字体乱码的时候,可以使用英文下的写法
- 产品规格说明书怎么写_产品说明都不会写?亚马逊旺季请靠边站!
- 苹果手机连wifi跳不出来登录网页解决办法
- mysql 后缀 deleted,MySQL · 特性分析 · (deleted) 临时空间
- 计算机网络拨号,个人拨号上网宽带连接设置图文方法
- Jsp四种变量作用范围
- 【计算机视觉算法岗面经】“吐血”整理:2019秋招资料
- mysql获取上月26号_根据当前时间查询上月26号的日期 本月月25号的日期
- 【Android Studio】在Mac中更换JDK Location
- linux软考常用命令
- 狼人杀服务器修改,微信小程序版狼人杀+服务端系列(1)
- SAP FI/CO 成本中心类型与功能范围
- 单链表的Java简单实现
- Java毕业设计——基于SSM的图书馆座位预约管理系统占座系统 / java图书馆座位预约管理系统占座系统
- 服务器加固安全指导书
热门文章
- mysql bitmap index_[MySQL] mysql中bitmap的简单运用
- 入门级图论算法:洪水填充算法
- jquery在当前页面打印页面div内容实例
- 判断form表单里面的元素属性是否有数据_html form标签的action属性是什么意思?又有哪些用法?(附实例)...
- java接收rowtype类型_Java PhysType.getJavaRowType方法代码示例
- Linux笔记-解决iptables配置后,本机无法访问本机,本机无法访问其他主机问题
- Python笔记-房贷计算(本息和本金,每月还利息和每月还本金)及作图对比
- PHP笔记-双色球例子
- Qt文档阅读笔记-Semaphores Example解析(信号量并发实例)
- SQL工作笔记-达梦(MySQL)将一个模式(库)中的一个表迁入到其他模式(库)