导读:我很笨,但是我很快——计算机之所以计算能力如此出众,不在于其有多智能,而是因为它超快的执行速度,而多核心则可以进一步成倍的提高效率。在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快速入门相关推荐

  1. python多久能上手_小白学习Python,怎样能够快速入门上手

    原标题:小白学习Python,怎样能够快速入门上手 时至今日,Python已经成为最受欢迎的编程语言之一,清晰易读,适用广泛.在TIOBE 排行榜中位居第四,成为名副其实的人工智能第一语言. 那么零基 ...

  2. python编程基础语法-Python编程基础语法快速入门

    1. 课程咨询加老师助理微信:助理1微信: chenjinglei88 ,助理2微信: omf6757 2. 决定购买并想得到陈敬雷老师亲自指导(课程或自己项目难题均可)加老师微信: chenjing ...

  3. pdf python 3.7编程快速入门 潘中强_无python基础,这些书籍可以帮您快速入门。

    利用Python进行数据分析> 定 价:119 元 作者:韦斯·麦金尼(Wes McKinney)著;徐敬一译 ISBN:9787111603702 出 版 社:机械工业出版社 学习Python ...

  4. python编程语法教程-Python编程基础语法快速入门

    1. 课程咨询加老师助理微信:助理1微信: chenjinglei88 ,助理2微信: omf6757 2. 决定购买并想得到陈敬雷老师亲自指导(课程或自己项目难题均可)加老师微信: chenjing ...

  5. python自动化运维快速入门pdf下载_我爱电子书-《Python自动化运维快速入门》| pdf + epub + mobi + awz3, 高清版, 带目录,Kindle版, 多看精排版下载...

    Python自动化运维快速入门 豆 0.0分 资源最后更新于 2020-03-29 01:00:08 作者:郑征 出版社:出版社清华大学出版社 出版日期:2019-04 ISBN:9787302525 ...

  6. python快速入门答案-Python 开发 14 天快速入门

    专栏亮点 零基础学习,循序渐进:专栏将编程语言的学习路线提炼为基础.中级.高级三层,内容由易到难,循序渐进,简练而生动地为读者呈现知识点. 内容全面,提炼要义:从核心概念到高级知识点,包括基本数据结构 ...

  7. python编程入门电子书下载-Python编程基础如何快速入门?“附电子书下载”

    本文是一位程序员的Python学习心得分享,对于新手来说,如何快速入门始终是个难题.今天我们就来看看他的心得体会,相信会对你有所启发. 本人使用Python语言将近2年了,这2年的时间就是我自己被Py ...

  8. python 入门程序_非Python程序员的Python速成课程-如何快速入门

    python 入门程序 This article is for people who already have experience in programming and want to learn ...

  9. python r转义_Python快速入门系列之二:还学不会我直播跪搓衣板

    Python作为一个,目前最火的编程语言之一,已经渗透到了各行各业.它易学好懂,拥有着丰富的库,功能齐全.人生苦短,就用Python. 这个快速入门系列分为六篇,包含了Python大部分基础知识,每篇 ...

最新文章

  1. 儿智展---国内首个少儿智能产品专业展11月登陆上海
  2. Android的数据存储
  3. 1、python的基础
  4. Oracle goldengate 11g错误汇总
  5. 1000万存在银行,一年的利息够日常生活费吗?
  6. vendor自动恢复_push文件到system分区,重启后文件会被自动恢复
  7. 理想汽车致歉并宣布召回10469台车,官方:不影响后续生产和销售
  8. rtl8187L驱动在linux2.6.35上的编译
  9. 计算机组成原理学习笔记第1章计算机系统概论 1.1——冯 · 诺依曼结构计算机工作原理及层次结构分析
  10. 摄像头视场角-对角线视场角-水平视场角
  11. 常用网站提交入口汇总让互联网收录你的网站
  12. WP-Super-Cache的使用
  13. 腾讯android 热更新,Android 腾讯 Bugly 热更新
  14. 八年级地理上册复习提纲(星球版)
  15. win python 判断 所有 子进程 结束_python 多进程如何终止或重启子进程?
  16. JS中正则表达式常用语法总结
  17. 数学建模笔记(十五):多元统计分析及R语言建模(判别分析、聚类分析、主成分分析、因子分析,含数据代码注释,均可供运行)
  18. 江西省南昌市谷歌高清卫星地图下载(百度网盘离线包下载)
  19. PAT结构与算法7-46 新浪微博热门话题 (50行精简 测试点分析)
  20. python新手代码大全.pdf,python新手代码及作用

热门文章

  1. 搞笑漫画:程序员的逻辑
  2. 编辑距离算法原理及其实现
  3. win10系统更新完部分电脑亮度无法调节或者找不到模块
  4. OnTimeAction
  5. C/C++抽红包系统
  6. 芯片IC测试专栏—ESD与TLP
  7. PDF格式分析(一)简介
  8. 3.1 腾讯云AI能力矩阵
  9. 特征工程 特征选择 reliefF算法
  10. WebRtc以Trickle ICE形式去进行pair