Python并发:多线程与多进程的详解


Posted in Python onJanuary 24, 2019

本篇概要

1.线程与多线程

2.进程与多进程

3.多线程并发下载图片

4.多进程并发提高数字运算

关于并发

在计算机编程领域,并发编程是一个很常见的名词和功能了,其实并发这个理念,最初是源于铁路和电报的早期工作。比如在同一个铁路系统上如何安排多列火车,保证每列火车的运行都不会发生冲突。

后来在20世纪60年代,学术界对计算机的并行计算开始进行研究,再后来,操作系统能够进行并发的处理任务,编程语言能够为程序实现并发的功能。

线程与多线程

什么是线程

一个线程可以看成是一个有序的指令流(完成特定任务的指令),并且可以通过操作系统来调度这些指令流。

线程通常位于进程程里面,由一个程序计数器、一个堆栈和一组寄存器以及一个标识符组成。这些线程是处理器可以分配时间的最小执行单元。

线程之间是可以共享内存并且互相通信的。但是当两个线程之间开始共享内存,就无法保证线程执行的顺序,这可能导致程序错误,或者产生错误的结果。这个问题我们日后会专门提及。

下面这个图片展示了多个线程在多个CPU中的存在方式:

Python并发:多线程与多进程的详解

线程的类型

在一个典型的操作系统里面,一般会有两种类型的线程:

1.用户级线程:我们能够创建、运行和杀死的线程;

2.内核级线程:操作系统运行的低级别线程;

Python工作在用户级线程上,我们介绍的内容也主要是在用户级的线程上运行的。

什么是多线程

现在的CPU基本上都是多线程的CPU,比如我们随意从京东上找一个Inter的酷睿i5处理器,看看它的产品规格:

Python并发:多线程与多进程的详解

这些CPU能够同时运行多个线程来处理任务,其实从本质上来说,这些CPU是利用一个能够在多个线程之间快速切换的单个内核来完成多线程的运行的,切换线程的速度足够快,所以我们并不会感觉到。但实质上,它们并不是同时运行的。

为了形象的理解多线程,我们来回忆一个场景。

在大学时代,期末的时候,有些科目的老师为了不为难大家,把考试设为开卷考试,不知道大家面对开卷考试的时候,做题的顺序是怎样的?

在单线程的工作模式下,我们从选择题到填空题到简答题再到分析题,一个一个按顺序的写。

遇到一个特别难的题目,我们就要翻书翻资料了,当然既然是开卷考试,有些题目的答案就不可能直接出现在教科书中,那么我们就要花费更多的时间来找答案,直到考试结束,因为某个难题耗费的翻书时间太多,导致后面一些简单的题目也没用做,嗯,开卷都写不完试卷,挂科名额就给你了。

而在多线程的工作模式下,我们也是按顺序写,但是遇到难题时,我们会稍微从书中找找答案,如果没找到,就先做下面的题目,把会做的题目做好,做好了容易的题目,再回到那个难题上,仔细从书中的蛛丝马迹中找答案。

在这个例子里面,我们只是一个人来完成,如果想要更快地完成考试,就得跟其他同学通力合作和分工了。

让我们看看线程的一些优点:

1.多线程能够有效提升I/O阻塞型程序的效率;

2.与进程相比,占用的系统资源少;

3.线程间能够共享资源,方便进行通信;

线程还有一些缺点:

1.Python中有全局解释器锁(GIL)的限制;

2.虽然线程之间能够进行通信,但是容易导致程序结果出错,使用的时候必须小心;

3.在多线程之间切换的计算代价高,会导致程序的整体性能下降。

进程与多进程

进程在本质上与线程非常相似,进程几乎可以完成线程能够完成的任何事情。

按照上面开卷考试的例子,如果我们和室友组成一个小团伙,那么我们就有四个CPU(4个人),四个人分别写和找不同的答案,这样考试的效率会提高很多。

一个进程里面,包含一个主线程,还可以生成很多子线程,每个线程都包含自己的寄存器组合堆栈。如果有需要的话,可以将它们组成多线程。

下面是单线程单进程和多线程单进程的示例:

Python并发:多线程与多进程的详解

进程的特性

一个进程通常包含以下的内容:

1.进程ID,进程组ID,用户ID,组ID

2.环境

3.工作目录

4.程序指令

5.寄存器

6.堆栈

7.文件描述

8.进程间通信工具

9.等等……

进程有以下优点:

1.更好地利用多核处理器;

2.在处理CPU密集型任务时比多线程要好;

3.可以通过多进程来避免全局解释器锁(GIL)的局限;

4.崩溃的进程不会导致整个程序的崩溃;

同时,还有以下缺点:

1.进程之间没有共享资源;

2.进程需要消耗更多的内存;

多进程

在Python中我们可以使用多线程或者多进程的方式来运行我们的代码以改进传统的单线程方式的性能。

在单核的CPU上可以使用多线程提高处理能力,但是在现在的计算机CPU中,多核处理器早已普及,为了有效的利用机器的资源,我们有必要使用多进程来发挥机器的价值。

一个CPU内核将任务分配给其他CPU:

Python并发:多线程与多进程的详解

通过Python的进程处理模块multiprocessing,我们可以有效的利用机器上所有的处理器,这有助于我们在处理CPU密集型任务时获得更高的性能。

使用multiprocessing模块,查看我们机器上的CPU核心数量:

Python并发:多线程与多进程的详解

结果返回一个数字,为CPU核心数。

多进程不仅能够提高我们的计算机的利用率,还能够避免全局解释器锁的限制,一个潜在的缺点是多进程间不能进行共享和通信(可以通过其他手段实现),但是这个缺点同时也使多进程更加容易使用和避免出现崩溃。

Python的局限性

在文章的前面,我们谈到了在Python中存在的全局解释器锁GIL的局限性。那GIL到底是个什么东西?

GIL本质上是一个互斥锁,它可以防止多个线程同时执行Python代码。 它是一个只能由一个线程保持的锁,如果你想要一个线程去执行代码,那么在它执行代码之前,首先必须获得这个锁。 这样做的一个好处是,当它被锁定的时候,没有别的进程可以同时运行代码,一定程度上避免了线程间的冲突:

Python并发:多线程与多进程的详解

上面这个图说明了多个线程如何被GIL阻塞。每个线程必须等待获取到GIL才能进行下一步的运行,然后再释放GIL。线程之间使用随机循环的方式,所以并不能控制和保证哪个线程会先得到GIL。

这样的设计似乎很反人类,而这也是很多人诟病Python的地方。但是,这个设计确实是保证的多线程之间的内存安全。

现在我们已经了解了线程和进程,以及Python的一些限制,现在是时候了解一下我们如何在应用程序中使用多线程多进程,以提高程序的速度。

并发文件下载

毫无疑问的,展现多线程优点的一个例子就是使用多线程来下载多个图片或者文件,由于I/O的阻塞性质,下载任务可能是多线程最佳的运用场景了。

http://tool.bitefu.net/jiari/data/2017.txt是一个提供2017年所有节假日的文本文件:

Python并发:多线程与多进程的详解

我们访问10次,获得10次文本文件,然后保存在本地。

先看看一个普通的爬取:

Python并发:多线程与多进程的详解

我们引入了模块urllib.request,然后创建了一个函数downloadImage()用于下载文件,创建了一个函数main()用于对下载函数进行遍历20次。

Python并发:多线程与多进程的详解

耗时4秒多。

下面看看使用多线程的:

Python并发:多线程与多进程的详解

程序的前部分大同小异,后面我们创建了一个threads列表,,然后遍历10次,创建一个新的线程对象,将其添加到threads列表中,然后启动该线程。

最后,我们通过遍历我们的threads列表来调用我们的线程,然后调用join()方法在每个线程上,这确保我们在下载完文件之前,不会执行剩下的代码。

Python并发:多线程与多进程的详解

运行代码,可以发现程序几乎同时启动了10个下载任务,然后在图片下载完成后,再打印出来。

耗时0.1秒,效率提高很多。

但是需要注意的是,在网络中进行文件IO,还需要考虑网络状况和自身机器的影响,不同的网络状况下,完成的效率也不一样。

并发数字运算

I/O密集型的任务适合于多线程,而CPU密集型的任务则适合用多进程。

在下面的例子里,我们将找出100万个20000到100000000之间随机数的质数。

顺序运算:

Python并发:多线程与多进程的详解

Python并发:多线程与多进程的详解

耗时18秒。

多进程运算:

Python并发:多线程与多进程的详解

Python并发:多线程与多进程的详解

耗时11秒。

我们分别按顺序循环100万遍和使用多进程的进程池循环100万次,多进程模式下速度提升了近7秒。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对三水点靠木的支持。如果你想了解更多相关内容请查看下面相关链接

Python 相关文章推荐
Python缩进和冒号详解
Jun 01 Python
python通过cookie模拟已登录状态的初步研究
Nov 09 Python
Python内置函数reversed()用法分析
Mar 20 Python
基于python的图片修复程序(实现水印去除)
Jun 04 Python
python实现俄罗斯方块游戏
Mar 25 Python
java中的控制结构(if,循环)详解
Jun 26 Python
Python定时发送天气预报邮件代码实例
Sep 09 Python
Python通过递归获取目录下指定文件代码实例
Nov 07 Python
python numpy 矩阵堆叠实例
Jan 17 Python
Django多数据库配置及逆向生成model教程
Mar 28 Python
基于Python的图像阈值化分割(迭代法)
Nov 20 Python
jupyter notebook更换皮肤主题的实现
Jan 07 Python
python用opencv批量截取图像指定区域的方法
Jan 24 #Python
python+pyqt5实现KFC点餐收银系统
Jan 24 #Python
Python微医挂号网医生数据抓取
Jan 24 #Python
Python实现查找二叉搜索树第k大的节点功能示例
Jan 24 #Python
几行Python代码爬取3000+上市公司的信息
Jan 24 #Python
python安装pywin32clipboard的操作方法
Jan 24 #Python
Python中extend和append的区别讲解
Jan 24 #Python
You might like
精致的人儿就要挑杯子喝咖啡
2021/03/03 冲泡冲煮
BBS(php & mysql)完整版(七)
2006/10/09 PHP
php zend 相对路径问题
2009/01/12 PHP
PHP使用curl模拟post上传及接收文件的方法
2016/03/04 PHP
Laravel监听数据库访问,打印SQL的例子
2019/10/24 PHP
js 颜色选择器(兼容firefox)
2009/03/05 Javascript
基于jquery1.4.2的仿flash超炫焦点图播放效果
2010/04/20 Javascript
javascript代码运行不出来执行错误的可能情况整理
2013/10/18 Javascript
javascript判断数组内是否重复的方法
2015/04/21 Javascript
让JavaScript中setTimeout支持链式操作的方法
2015/06/19 Javascript
JavaScript运动减速效果实例分析
2015/08/04 Javascript
深入解析JavaScript中的arguments对象
2016/06/12 Javascript
Javascript 跨域知识详细介绍
2016/10/30 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
2017/02/27 Javascript
js实现多行文本框统计剩余字数功能
2017/03/28 Javascript
vue2实现移动端上传、预览、压缩图片解决拍照旋转问题
2017/04/13 Javascript
基于Vue实现页面切换左右滑动效果
2020/06/29 Javascript
JS分页的实现(同步与异步)
2017/09/16 Javascript
Mongoose中document与object的区别示例详解
2017/09/18 Javascript
小程序兼容安卓和IOS数据处理问题及坑
2018/09/18 Javascript
利用d3.js制作连线动画图与编辑器的方法实例
2019/09/05 Javascript
JS实现分页导航效果
2020/02/19 Javascript
Python实现TCP/IP协议下的端口转发及重定向示例
2016/06/14 Python
python实现随机调用一个浏览器打开网页
2018/04/21 Python
python清除函数占用的内存方法
2018/06/25 Python
PyQt5的QWebEngineView使用示例
2020/10/20 Python
MoviePy常用剪辑类及Python视频剪辑自动化
2020/12/18 Python
随机分配座位,共50个学生,使学号相邻的同学座位不能相邻
2014/01/18 面试题
中级会计职业生涯规划范文
2014/01/16 职场文书
教师自我鉴定范文
2014/03/20 职场文书
总结表彰大会主持词
2014/03/26 职场文书
厉行勤俭节约倡议书
2014/05/16 职场文书
酒店管理求职信
2014/06/09 职场文书
活动总结结尾怎么写
2014/08/30 职场文书
劳动争议和解协议书范本
2014/11/20 职场文书
和谐拯救危机观后感
2015/06/15 职场文书