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基于回溯法解决01背包问题实例
Dec 06 Python
python实现随机梯度下降(SGD)
Mar 24 Python
flask中主动抛出异常及统一异常处理代码示例
Jan 18 Python
python在html中插入简单的代码并加上时间戳的方法
Oct 16 Python
对Python生成器、装饰器、递归的使用详解
Jul 19 Python
Python使用Pandas对csv文件进行数据处理的方法
Aug 01 Python
python logging添加filter教程
Dec 24 Python
From CSV to SQLite3 by python 导入csv到sqlite实例
Feb 14 Python
浅谈python处理json和redis hash的坑
Jul 16 Python
python Paramiko使用示例
Sep 21 Python
python中Matplotlib绘制直线的实例代码
Jul 04 Python
Python Matplotlib绘制等高线图与渐变色扇形图
Apr 14 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
超神学院:鹤熙已踏入神圣领域,实力不比凯莎弱
2020/03/02 国漫
dedecms系统的广告设置代码 基础版本
2010/04/09 PHP
PHP 类与构造函数解析
2017/02/06 PHP
php实现与python进行socket通信的方法示例
2017/08/30 PHP
javascript 浏览器判断 绑定事件 arguments 转换数组 数组遍历
2009/07/06 Javascript
JS简单实现元素复制示例附图
2013/11/19 Javascript
Javascript window对象详解
2014/11/12 Javascript
JS显示日历和天气的方法
2016/03/01 Javascript
jQuery.deferred对象使用详解
2016/03/18 Javascript
vue表单绑定实现多选框和下拉列表的实例
2017/08/12 Javascript
JavaScript实现HTML5游戏断线自动重连的方法
2017/09/18 Javascript
Bootstrap实现下拉菜单多级联动
2017/11/23 Javascript
基于JS实现带动画效果的流程进度条
2018/06/01 Javascript
Angular2之二级路由详解
2018/08/31 Javascript
防止Layui form表单重复提交的实现方法
2019/09/10 Javascript
基于Cesium绘制抛物弧线
2020/11/18 Javascript
vue使用require.context实现动态注册路由
2020/12/25 Vue.js
python3使用urllib示例取googletranslate(谷歌翻译)
2014/01/23 Python
基于Python 的进程管理工具supervisor使用指南
2016/09/18 Python
TensorFlow 模型载入方法汇总(小结)
2018/06/19 Python
python GUI图形化编程wxpython的使用
2019/07/19 Python
关于pandas的离散化,面元划分详解
2019/11/22 Python
基于h5py的使用及数据封装代码
2019/12/26 Python
python+OpenCV实现图像拼接
2020/03/05 Python
python+Selenium自动化测试——输入,点击操作
2020/03/06 Python
python 错误处理 assert详解
2020/04/20 Python
Python3.9.0 a1安装pygame出错解决全过程(小结)
2021/02/02 Python
python 实现网易邮箱邮件阅读和删除的辅助小脚本
2021/03/01 Python
html5的画布canvas——画出弧线、旋转的图形实例代码+效果图
2013/06/09 HTML / CSS
goodhealth官方海外旗舰店:新西兰国民营养师
2017/12/15 全球购物
水果花束:Fruit Bouquets
2017/12/20 全球购物
国际奢侈品品牌童装购物网站:Designer Childrenswear
2019/05/08 全球购物
美国最大最全的亚洲购物网站:美国亚米网(Yamibuy)
2020/05/05 全球购物
护士实习鉴定范文
2013/12/22 职场文书
旅游管理专业个人求职信范文
2013/12/24 职场文书
群众路线教师自我剖析材料
2014/09/29 职场文书