python并发之concurrent快速入门
导读:我很笨,但是我很快——计算机之所以计算能力如此出众,不在于其有多智能,而是因为它超快的执行速度,而多核心则可以进一步成倍的提高效率。在python中,concurrent库就是用于完成并发的模块之一。
01 初识concurrent
concurrent库是python内置模块之一,基于threading和multiprocessing两个模块实现,并对二者进行了很好的封装和集成,使其拥有更加简洁易用的接口函数,无需再考虑start()、join()、lock()等问题。
打开concurrent模块(默认安装位于..\Python\Python37\Lib),发现当前其仅内置了一个futures子模块,而futures子模块中,则有3个重要的.py文件,其中_base.py是最主要的模块,提供了大部分并发功能,但属于私有模块,不能被其他程序直接import,另外两个则是process和thread模块,即多进程和多线程,二者均调用_base实现主要并发接口函数。
concurrent英文原义为"并发的",futures英文原义为"未来",模块取名concurrent很好理解(java中有同名包),而子模块取名futures则用以表示未来有待完成的任务,似乎也正体现了多线程/多进程中任务队列的含义。
注:关于多线程和多进程的理解和区别本文不予展开,网上有很多通俗易懂的讲解可供查找学习。
02 Executor
Executor是concurrent.futures模块的抽象类,但一般不直接调用,而是为线程池和进程池提供了一个父类,即ThreadPoolExecutor和ProcessPoolExecutor均继承自Executor。
Executor虽然不直接调用,但却提供了几个非常重要的接口供其子类继承
submit(fn, *args, **kwarg):用于调用并发任务,其中参数fn是执行任务的函数,通过fn(*args **kwargs)的形式执行单个任务,返回Future对象
map(func, *iterables, timeout=None, chunksize=1):类似于python全局函数map,将可迭代对象异步并行映射给func函数,并返回一个新的可迭代结果。其中可通过timeout设置允许最大单个任务的延时,chunksize用于在多进程中设置分组规模,在多线程中无意义
shutdown(wait=True),用于在任务完成后释放所调用的资源,其中wait参数默认为True,表示当前任务执行完毕且释放已分配资源后才返回,wait设置为False时则执行shutdown后立即返回,实际不怎么应用的到。后文将会提到,由于excutor支持上下文管理器with方法,所以可避免显式调用shutdown函数。
Executor的这几个方法中,submit()和map()也是ThreadPoolExecutor和ProcessPoolExecutor两个子类的常用方法。
另外,与Executor同在_base.py模块中定义的还有future类(调度并发任务后生成对象,用于获取单个任务信息)、wait()方法(其功能类似利用threading模块实现多线程时的join方法)等,具体不再展开。
03 ThreadPoolExecutor
ThreadPoolExecutor 是 Executor 的子类,即线程池对象类,用来异步执行调度并发任务。
初始化
1def __init__(self, max_workers=None, thread_name_prefix='', initializer=None, initargs=()):
2 pass
其中max_workers是最主要和最常用的初始化参数,用于设置最大线程个数,默认为CPU个数乘以5,thread_name_prefix用于设置线程名前缀,后两个初始参数为3.7版本中增加,用于在每个任务初始化时调用一个可选对象,实际一般不用。
1if max_workers is None:
2 # Use this number because ThreadPoolExecutor is often
3 # used to overlap I/O instead of CPU work.
4 max_workers = (os.cpu_count() or 1) * 5
执行多线程任务
执行多线程任务有两种方式,都是继承自父类Executor中的方法,分别是submit()和map()
1from concurrent.futures import ThreadPoolExecutor as executor
2futures = [executor.submit(fun, arg) for arg in args]#方式1
3results = executor.map(fun, args)#方式2
获取多线程调用结果
在使用submit执行多线程任务时,每个线程任务返回一个future对象,future对象是一个用于接收单个任务执行结果的对象,其result()方法常用于获取单任务执行结果,例如
1futures = [executor.submit(fun, arg) for arg in args]#方式1
2results = [future.result() for future in futures]
而在map执行方式中,则是直接返回单个任务执行结果的迭代器。
submit与map对比:二者均可用于执行线程池任务并返回结果,区别是后者直接返回执行结果;而前者返回一个future对象,在future对象中,除了可用其result()方法获得执行结果外,还有详细的方法来获取和设置任务状态,如
cancel():尝试取消调用
cancelled():如果调用被成功取消返回True
running():如果当前正在被执行不能被取消返回True
done():如果调用被成功取消或者完成running返回True
04 ThreadPoolExecutor
与ThreadPoolExecutor类似,ProcessPoolExecutor进程池也是继承自Executor类的一个子类,且很多调用接口和执行方式与前者几乎一致。
ProcessPoolExecutor官方文档内置配图
初始化
1def __init__(self, max_workers=None, mp_context=None, initializer=None, initargs=()):
2 pass
3 if max_workers is None:
4 self._max_workers = os.cpu_count() or 1
这里,最大进程数默认为CPU核心个数。第二个参数与线程池类不同,是用于初始化一个多进程环境,默认调用multiporcessing模块的get_context方法。
执行多进程任务:用submit或map方法,具体与多线程调用方式一致
获取执行结果:与多线程获取结果方式一致
05 并发实战对比
对python多线程和多进程并发任务有所了解的都知道,对于IO密集型任务(如涉及磁盘读写较多的任务、网络响应和传输较多的下载任务等),多线程和多进程都能带来较高的并发效率,但是对于计算密集型(CPU密集型)任务(涉及的任务主要是依赖CPU计算),则多线程一般不会带来效率上的提升,甚至与串行几乎一致。
下面通过两个实例验证这一结论,并测试并发效率
IO密集型
我们以python爬虫请求10次网页为例,分别测试串行、多线程和多进程3种方式的执行时间
1from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor2import math3from time import time4import requests56URLS = ['https://www.baidu.com/']*1078def get_baidu(url):9 return requests.get(url).text
10
11def multi_thread():
12 with ThreadPoolExecutor() as executor:
13 return list(executor.map(get_baidu, URLS))
14
15def multi_process():
16 with ProcessPoolExecutor() as executor:
17 return list(executor.map(get_baidu, URLS))
18
19def single():
20 return list(map(get_baidu, URLS))
21
22if __name__ == '__main__':
23 start = time()
24 single()
25 print("time used by single computing :", time()-start)
26 start = time()
27 multi_thread()
28 print("time used by multi_thread computing :", time()-start)
29 start = time()
30 multi_process()
31 print("time used by multi_process computing :", time()-start)
32"""
33time used by single computing : 7.0965657234191895
34time used by multi_thread computing : 0.41477227210998535
35time used by multi_process computing : 1.7192769050598145
36"""
计算密集型
这里,我们选用官方demo,即判断一个数是否是质数的案例。
1from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor2import math3from time import time45PRIMES = [6 112272535095293,7 112582705942171,8 112272535095293,9 115280095190773,
10 115797848077099,
11 1099726899285419]
12
13def is_prime(n):
14 pass#具体可参照官网或后台回复concurrent下载
15
16def multi_thread():
17 with ThreadPoolExecutor() as executor:
18 return list(executor.map(is_prime, PRIMES))
19
20def multi_process():
21 with ProcessPoolExecutor() as executor:
22 return list(executor.map(is_prime, PRIMES))
23
24def single():
25 return list(map(is_prime, PRIMES))
26
27if __name__ == '__main__':
28 start = time()
29 single()
30 print("time used by single computing :", time()-start)
31 start = time()
32 multi_thread()
33 print("time used by multi_thread computing :", time()-start)
34 start = time()
35 multi_process()
36 print("time used by multi_process computing :", time()-start)
37"""
38time used by single computing : 3.2942192554473877
39time used by multi_thread computing : 3.2454559803009033
40time used by multi_process computing : 2.2647616863250732
41"""
注:以上两个详细源码可在 公众号:小数志 后台回复"concurrent"下载
串行调度计算密集型任务,CPU负载曲线(33%左右)
多线程调度计算密集型任务,CPU负载曲线(33%左右)
多进程调度计算密集型任务,CPU负载曲线(100%左右)
结论:
对于IO密集型任务,多线程可发挥巨大威力,甚至执行效率超过了多进程执行方式(案例中多线程效率超过多进程的原因有二:一是进程间切换相较于线程切换带来更大开销和时延,二是默认初始化参数中多线程数量是CPU核心数的5倍,而多进程数量等于CPU核心数)
对于计算密集型任务,多线程由于仅调用单个CPU进行计算,所以效率与串行几乎一致,而多进程由于可以调用多个CPU的计算能力,效率要更高一些。但由于进程间切换需要开销,故其与串行效率的比值达不到核心个数(经测试,数据量足够大时,效率比值接近CPU核心数)。
06 总结
concurrent模块主要类和方法关系图
python自带concurrent模块实现了对多线程threading模块和多进程multiprocessing模块的高度封装和集成,使用极为方便
ThreadPoolExecutor类和ProcessPoolExecutor类均继承自Executor父类,二者初始化方式略有区别,但调度并发任务和获取执行结果方式几乎一致
2种调度并发任务的方式:submit()和map()
submit()相比map而言,具有更丰富的任务定制方法
IO密集型任务多线程和多进程均能带来较高执行效率,而计算密集型任务则仅多进程能带来实际提升
相关阅读:
1. 多种爬虫方式对比
2. pyquery5行代码爬取百度热点
3. 5分钟速览python正则表达式
4. 我找到了二分法"作弊"的接口
5. Python的内置容器不止有list/dict
6. 地图可视化不只是pyecharts.map
7. 生成词云的几种方式
python并发之concurrent快速入门相关推荐
- python多久能上手_小白学习Python,怎样能够快速入门上手
原标题:小白学习Python,怎样能够快速入门上手 时至今日,Python已经成为最受欢迎的编程语言之一,清晰易读,适用广泛.在TIOBE 排行榜中位居第四,成为名副其实的人工智能第一语言. 那么零基 ...
- python编程基础语法-Python编程基础语法快速入门
1. 课程咨询加老师助理微信:助理1微信: chenjinglei88 ,助理2微信: omf6757 2. 决定购买并想得到陈敬雷老师亲自指导(课程或自己项目难题均可)加老师微信: chenjing ...
- pdf python 3.7编程快速入门 潘中强_无python基础,这些书籍可以帮您快速入门。
利用Python进行数据分析> 定 价:119 元 作者:韦斯·麦金尼(Wes McKinney)著;徐敬一译 ISBN:9787111603702 出 版 社:机械工业出版社 学习Python ...
- python编程语法教程-Python编程基础语法快速入门
1. 课程咨询加老师助理微信:助理1微信: chenjinglei88 ,助理2微信: omf6757 2. 决定购买并想得到陈敬雷老师亲自指导(课程或自己项目难题均可)加老师微信: chenjing ...
- python自动化运维快速入门pdf下载_我爱电子书-《Python自动化运维快速入门》| pdf + epub + mobi + awz3, 高清版, 带目录,Kindle版, 多看精排版下载...
Python自动化运维快速入门 豆 0.0分 资源最后更新于 2020-03-29 01:00:08 作者:郑征 出版社:出版社清华大学出版社 出版日期:2019-04 ISBN:9787302525 ...
- python快速入门答案-Python 开发 14 天快速入门
专栏亮点 零基础学习,循序渐进:专栏将编程语言的学习路线提炼为基础.中级.高级三层,内容由易到难,循序渐进,简练而生动地为读者呈现知识点. 内容全面,提炼要义:从核心概念到高级知识点,包括基本数据结构 ...
- python编程入门电子书下载-Python编程基础如何快速入门?“附电子书下载”
本文是一位程序员的Python学习心得分享,对于新手来说,如何快速入门始终是个难题.今天我们就来看看他的心得体会,相信会对你有所启发. 本人使用Python语言将近2年了,这2年的时间就是我自己被Py ...
- python 入门程序_非Python程序员的Python速成课程-如何快速入门
python 入门程序 This article is for people who already have experience in programming and want to learn ...
- python r转义_Python快速入门系列之二:还学不会我直播跪搓衣板
Python作为一个,目前最火的编程语言之一,已经渗透到了各行各业.它易学好懂,拥有着丰富的库,功能齐全.人生苦短,就用Python. 这个快速入门系列分为六篇,包含了Python大部分基础知识,每篇 ...
最新文章
- 儿智展---国内首个少儿智能产品专业展11月登陆上海
- Android的数据存储
- 1、python的基础
- Oracle goldengate 11g错误汇总
- 1000万存在银行,一年的利息够日常生活费吗?
- vendor自动恢复_push文件到system分区,重启后文件会被自动恢复
- 理想汽车致歉并宣布召回10469台车,官方:不影响后续生产和销售
- rtl8187L驱动在linux2.6.35上的编译
- 计算机组成原理学习笔记第1章计算机系统概论 1.1——冯 · 诺依曼结构计算机工作原理及层次结构分析
- 摄像头视场角-对角线视场角-水平视场角
- 常用网站提交入口汇总让互联网收录你的网站
- WP-Super-Cache的使用
- 腾讯android 热更新,Android 腾讯 Bugly 热更新
- 八年级地理上册复习提纲(星球版)
- win python 判断 所有 子进程 结束_python 多进程如何终止或重启子进程?
- JS中正则表达式常用语法总结
- 数学建模笔记(十五):多元统计分析及R语言建模(判别分析、聚类分析、主成分分析、因子分析,含数据代码注释,均可供运行)
- 江西省南昌市谷歌高清卫星地图下载(百度网盘离线包下载)
- PAT结构与算法7-46 新浪微博热门话题 (50行精简 测试点分析)
- python新手代码大全.pdf,python新手代码及作用