初识

Python中已经有了threading模块,为什么还需要线程池呢,线程池又是什么东西呢?在介绍线程同步的信号量机制的时候,举得例子是爬虫的例子,需要控制同时爬取的线程数,例子中创建了20个线程,而同时只允许3个线程在运行,但是20个线程都需要创建和销毁,线程的创建是需要消耗系统资源的,有没有更好的方案呢?其实只需要三个线程就行了,每个线程各分配一个任务,剩下的任务排队等待,当某个线程完成了任务的时候,排队任务就可以安排给这个线程继续执行。

这就是线程池的思想(当然没这么简单),但是自己编写线程池很难写的比较完美,还需要考虑复杂情况下的线程同步,很容易发生死锁。从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutorProcessPoolExecutor两个类,实现了对threadingmultiprocessing的进一步抽象(这里主要关注线程池),不仅可以帮我们自动调度线程,还可以做到:

  1. 主线程可以获取某一个线程(或者任务的)的状态,以及返回值。
  2. 当一个线程完成的时候,主线程能够立即知道。
  3. 让多线程和多进程的编码接口一致。

实例

简单使用

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,那么先提交的是task1task2还在排队等候,这是时候就可以成功取消。
  • 使用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线程池(实例)相关推荐

  1. Python ThreadPoolExecutor线程池

    概念 Python中已经有了threading模块,为什么还需要线程池呢,线程池又是什么东西呢?以爬虫为例,需要控制同时爬取的线程数,例子中创建了20个线程,而同时只允许3个线程在运行,但是20个线程 ...

  2. Python运用线程池ThreadPoolExecutor多线程下载搜索图片

    用chrome解析某搜索网站图片搜索请求包,再参考网上一些已有的代码,在vscode+python3.7环境,多线程下载搜索结果页面上图片的代码,其中加入了键盘中止和ThreadPoolExecuto ...

  3. async spring 默认线程池_Spring boot注解@Async线程池实例详解

    这篇文章主要介绍了Spring boot注解@Async线程池实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 从Spring3开始提供了@A ...

  4. 13.ThreadPoolExecutor线程池之submit方法

    jdk1.7.0_79  在上一篇<ThreadPoolExecutor线程池原理及其execute方法>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法 ...

  5. Java多线程学习总结(4)——ThreadPoolExecutor 线程池的拒绝策略学习总结

    前言 谈到java的线程池最熟悉的莫过于ExecutorService接口了,jdk1.5新增的java.util.concurrent包下的这个api,大大的简化了多线程代码的开发.而不论你用Fix ...

  6. ThreadPoolExecutor线程池原理

    ThreadPoolExecutor线程池原理 线程池原理 1. 线程池的简单介绍 1.1 线程池是什么 1.2 线程池解决的核心问题是什么 2. 线程池的实现原理 2.1 线程池的执行流程 2.2 ...

  7. Python实现线程池

    Python实现线程池 最近在做一些文本处理方面的事情,考虑到程序利用并发性可以提高执行效率(不纠结特殊反例),于是入围的Idea如使用多进程或多线程达到期望的目标,对于进程或线程的创建是有代价的,那 ...

  8. ThreadPoolExecutor线程池,shutdown和shutdownNow关闭线程池方式对比,以及确保线程池能够彻底关闭的一种方式

    1. ThreadPoolExecutor线程池 1.1 创建线程池,构造方法的几个参数说明及创建如下. 1.2 shutdown方式关闭线程池 a. 空闲且能interrupt表示该线程处于阻塞等待 ...

  9. Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】

    基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...

最新文章

  1. 启明云端分享| ESP32-C3智能写字板应用解决方案
  2. Kubernetes安装之十:配置node节点之kube-proxy
  3. C语言中如何求一天是星期几,计算任何一天是星期几的C语言源代码.
  4. Spring Boot Admin –用于管理Spring Boot应用程序的Admin UI
  5. mysql profile 调试sql_SQL Server profile使用技巧
  6. 字体乱码的时候,可以使用英文下的写法
  7. 产品规格说明书怎么写_产品说明都不会写?亚马逊旺季请靠边站!
  8. 苹果手机连wifi跳不出来登录网页解决办法
  9. mysql 后缀 deleted,MySQL · 特性分析 · (deleted) 临时空间
  10. 计算机网络拨号,个人拨号上网宽带连接设置图文方法
  11. Jsp四种变量作用范围
  12. 【计算机视觉算法岗面经】“吐血”整理:2019秋招资料
  13. mysql获取上月26号_根据当前时间查询上月26号的日期 本月月25号的日期
  14. 【Android Studio】在Mac中更换JDK Location
  15. linux软考常用命令
  16. 狼人杀服务器修改,微信小程序版狼人杀+服务端系列(1)
  17. SAP FI/CO 成本中心类型与功能范围
  18. 单链表的Java简单实现
  19. Java毕业设计——基于SSM的图书馆座位预约管理系统占座系统 / java图书馆座位预约管理系统占座系统
  20. 服务器加固安全指导书

热门文章

  1. mysql bitmap index_[MySQL] mysql中bitmap的简单运用
  2. 入门级图论算法:洪水填充算法
  3. jquery在当前页面打印页面div内容实例
  4. 判断form表单里面的元素属性是否有数据_html form标签的action属性是什么意思?又有哪些用法?(附实例)...
  5. java接收rowtype类型_Java PhysType.getJavaRowType方法代码示例
  6. Linux笔记-解决iptables配置后,本机无法访问本机,本机无法访问其他主机问题
  7. Python笔记-房贷计算(本息和本金,每月还利息和每月还本金)及作图对比
  8. PHP笔记-双色球例子
  9. Qt文档阅读笔记-Semaphores Example解析(信号量并发实例)
  10. SQL工作笔记-达梦(MySQL)将一个模式(库)中的一个表迁入到其他模式(库)