实例代码讲解Python 线程池


Posted in Python onAugust 24, 2020

大家都知道当任务过多,任务量过大时如果想提高效率的一个最简单的方法就是用多线程去处理,比如爬取上万个网页中的特定数据,以及将爬取数据和清洗数据的工作交给不同的线程去处理,也就是生产者消费者模式,都是典型的多线程使用场景。

那是不是意味着线程数量越多,程序的执行效率就越快呢。

显然不是。线程也是一个对象,是需要占用资源的,线程数量过多的话肯定会消耗过多的资源,同时线程间的上下文切换也是一笔不小的开销,所以有时候开辟过多的线程不但不会提高程序的执行效率,反而会适得其反使程序变慢,得不偿失。

所以,如何确定多线程的数量是多线程编程中一个非常重要的问题。好在经过多年的摸索业界基本已形成一套默认的标准。

对于 CPU 密集型的计算场景,理论上将线程的数量设置为 CPU 核数就是最合适的,这样可以将每个 CPU 核心的性能压榨到极致,不过在工程上,线程的数量一般会设置为 CPU 核数 + 1,这样在某个线程因为未知原因阻塞时多余的那个线程完全可以顶上。

而对于 I/O 密集型的应用,就需要考虑 CPU 计算的耗时和 I/O 的耗时比了。如果 I/O 耗时和 CPU 耗时 为 1:1,那么两个线程是最合适的,因为当 A 线程做 I/O 操作时,B 线程执行 CPU 计算任务,当 B 线程做 I/O 操作时,A 线程执行 CPU 计算任务,CPU 和 I/O 的利用率都得到了百分百,完美。所以可以认为最佳线程数 = CPU 核数 * [1 +(I/O 耗时 / CPU 耗时]。

线程池

平时我们自己写多线程程序时基本都是直接调用 Thread(target=method) 即可,实际上创建线程远没有这么简单,需要分配内存,同时线程还需要调用操作系统内核的 API,然后操作系统还需要为线程分配一系列的资源,过程很是复杂,所以要尽量避免频繁的创建和销毁线程。

回想一下自己平时写多线程代码的模式,是不是当任务来临时直接创建线程,执行任务,当任务执行结束之后,线程也就随之消亡了。然后又开始循环往复。有多少个任务就创建了多少个线程。这种模式的话很浪费硬件资源。

那如何避免这种问题呢,线程池就派上用场了。

其实线程池就是生产者消费者模式的最佳实践,当线程池初始化时,会自动创建指定数量的线程,有任务到达时直接从线程池中取一个空闲线程来用即可,当任务执行结束时线程不会消亡而是直接进入空闲状态,继续等待下一个任务。而随着任务的增加线程池中的可用线程必将逐渐减少,当减少至零时,任务就需要等待了。

在 python 中使用线程池有两种方式,一种是基于第三方库 threadpool,另一种是基于 python3 新引入的库 concurrent.futures.ThreadPoolExecutor。这里我们都做一下介绍。

threadpool 方式

使用 threadpool 前需要先安装一下,看了这么久我们的文章,相信你很快就会搞定的。在命令行执行如下命令即可。

pip install threadpool

以下是一个简易的线程池使用模版,我们创建了一个函数 sayhello,然后创建了一个大小为 2 的线程池,也就是线程池总共有两个活跃线程。

最后通过 pool.putRequest() 将任务丢到线程池执, pool.wait() 等待所有线程结束。同时我们还可以定义回调函数,拿到任务的返回结果。

由结果我们可以看出,线程池中的确只有两个线程,分别为 Thread-1Thread-2

import time
import threadpool
import threading

def sayhello(name):
  print("%s say Hello to %s" % (threading.current_thread().getName(), name));
  time.sleep(1)
  return name

def callback(request, result): # 回调函数,用于取回结果
  print("callback result = %s" % result)

name_list =['admin','root','scott','tiger']
start_time = time.time()
pool = threadpool.ThreadPool(2) # 创建线程池
requests = threadpool.makeRequests(sayhello, name_list, callback) # 创建任务
[pool.putRequest(req) for req in requests] # 加入任务
pool.wait() 
print('%s cost %d second' % (threading.current_thread().getName(), time.time()-start_time))

## 运行结果如下
Thread-1 say Hello to admin
Thread-2 say Hello to root
Thread-1 say Hello to scott
Thread-2 say Hello to tiger
callback result = admin
callback result = root
callback result = tiger
callback result = scott
MainThread cost 2 second

ThreadPoolExecutor 方式

ThreadPoolExecutor 是 python3 新引入的库,具体使用方法与 threadpool 大同小异,同样是创建容量为 2 的线程池,提交四个任务。只不过这里分别是通过 submit as_completed 来提交和获取任务返回结果的。

同样由输出结果我们可以看出,两种线程池的实现方式中关于线程的命名方式是不一致的。

import time
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed

def sayhello(name):
  print("%s say Hello to %s" % (threading.current_thread().getName(), name));
  time.sleep(1)
  return name

name_list =['admin','root','scott','tiger']
start_time = time.time()
with ThreadPoolExecutor(2) as executor: # 创建 ThreadPoolExecutor 
  future_list = [executor.submit(sayhello, name) for name in name_list] # 提交任务

for future in as_completed(future_list):
  result = future.result() # 获取任务结果
  print("%s get result : %s" % (threading.current_thread().getName(), result))

print('%s cost %d second' % (threading.current_thread().getName(), time.time()-start_time))

## 运行结果如下
ThreadPoolExecutor-0_0 say Hello to admin
ThreadPoolExecutor-0_1 say Hello to root
ThreadPoolExecutor-0_0 say Hello to scott
ThreadPoolExecutor-0_1 say Hello to tiger
MainThread get result : root
MainThread get result : tiger
MainThread get result : scott
MainThread get result : admin
MainThread cost 2 second

线程池总结

本文介绍了常用的两种线程池的实现方式,在多线程编程中能使用线程池就不要自己去创建线程,并不是说线程池实现的多么好,其实我们自己完全也可以实现一个功能更强大的线程池。但是其内置的线程池一来是受过全方面测试的,在安全性,性能和方便性上基本就是最优的了,同时线程池还替我们做了很多额外的工作,比如任务队列的维护,线程销毁时资源的回收等都不需要开发者去关心,我们只需注重业务逻辑即可,不需要在关心其他额外的工作,这将大大提高我们的的工作效率和使用感受。

当然其自带的线程池也不是十全十美的,至少暂时没有提供动态添加任务的入口出来。而且在设计方面不够灵活,比如我想线程池只维护一个核心数量,也就是上文说的最大数量。但是当任务过多时可以再额外创建出一些新的线程(阈值可以自定义),处理完之后这些多余的线程将自动销毁,目前这个是做不到的。

代码地址

https://github.com/JustDoPython/python-100-day/tree/master/day-053

参考资料

https://chrisarndt.de/projects/threadpool/api/

以上就是实例代码讲解Python 线程池的详细内容,更多关于Python 线程池的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python实现文件分组复制到不同目录的例子
Jun 04 Python
python从网络读取图片并直接进行处理的方法
May 22 Python
Python实现的排列组合计算操作示例
Oct 13 Python
python开发准备工作之配置虚拟环境(非常重要)
Feb 11 Python
pyqt5 lineEdit设置密码隐藏,删除lineEdit已输入的内容等属性方法
Jun 24 Python
python opencv 图像拼接的实现方法
Jun 27 Python
Python使用qrcode二维码库生成二维码方法详解
Feb 17 Python
python编程进阶之类和对象用法实例分析
Feb 21 Python
opencv之颜色过滤只留下图片中的红色区域操作
Jun 05 Python
python连接mysql有哪些方法
Jun 24 Python
Django 实现图片上传和下载功能
Dec 31 Python
Python中np.random.randint()参数详解及用法实例
Sep 23 Python
详解python UDP 编程
Aug 24 #Python
PyTorch如何搭建一个简单的网络
Aug 24 #Python
Python pysnmp使用方法及代码实例
Aug 24 #Python
详解python tcp编程
Aug 24 #Python
Python rabbitMQ如何实现生产消费者模式
Aug 24 #Python
利用Python的folium包绘制城市道路图的实现示例
Aug 24 #Python
深入分析python 排序
Aug 24 #Python
You might like
PHP 配置文件中open_basedir选项作用
2009/07/19 PHP
PHP similar_text 字符串的相似性比较函数
2010/05/26 PHP
PHP中使用substr()截取字符串出现中文乱码问题该怎么办
2015/10/21 PHP
jquery中常用的SET和GET
2009/01/13 Javascript
在IE下获取object(ActiveX)的Param的代码
2009/09/15 Javascript
Javascript中获取出错代码所在文件及行数的代码
2010/09/23 Javascript
jQuery插件slicebox实现3D动画图片轮播切换特效
2015/04/12 Javascript
js小数运算出现多位小数如何解决
2015/10/08 Javascript
10个JavaScript中易犯小错误
2016/02/14 Javascript
Jquery Easyui搜索框组件SearchBox使用详解(19)
2016/12/17 Javascript
jQuery设计思想
2017/03/07 Javascript
Node.js中sequelize时区的配置方法
2017/12/10 Javascript
微信小程序模板(template)使用详解
2018/01/31 Javascript
Nodejs把接收图片base64格式保存为文件存储到服务器上
2018/09/26 NodeJs
推荐一个基于Node.js的表单验证库
2019/02/15 Javascript
JS严格模式原理与用法实例分析
2020/04/27 Javascript
python opencv读mp4视频的实例
2018/12/07 Python
python设定并获取socket超时时间的方法
2019/01/12 Python
Python实现合并两个有序链表的方法示例
2019/01/31 Python
使用Pytorch来拟合函数方式
2020/01/14 Python
使用Pycharm在运行过程中,查看每个变量的操作(show variables)
2020/06/08 Python
html5 利用canvas手写签名并保存的实现方法
2018/07/12 HTML / CSS
HTML5之语义标签介绍
2016/07/07 HTML / CSS
英国顶级家庭折扣店:The Works
2017/09/06 全球购物
教师师德教育的自我评价
2013/10/31 职场文书
中学生差生评语
2014/01/30 职场文书
《梅花魂》教学反思
2014/04/30 职场文书
学校春季防火方案
2014/06/08 职场文书
高中生国庆节演讲稿范文2014
2014/09/21 职场文书
党员“四风”方面存在问题及整改措施
2014/09/24 职场文书
售房协议书范本2014
2014/10/23 职场文书
淮阳太昊陵导游词
2015/02/10 职场文书
考生诚信考试承诺书
2015/04/29 职场文书
行政诉讼答辩状
2015/05/21 职场文书
小学数学教学随笔
2015/08/14 职场文书
2016年父亲节寄语
2015/12/04 职场文书