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解决鸡兔同笼问题的方法
Dec 20 Python
python实现的文件同步服务器实例
Jun 02 Python
python+selenium实现京东自动登录及秒杀功能
Nov 18 Python
python+tkinter编写电脑桌面放大镜程序实例代码
Jan 16 Python
Python简单实现网页内容抓取功能示例
Jun 07 Python
python+selenium打印当前页面的titl和url方法
Jun 22 Python
flask session组件的使用示例
Dec 25 Python
Python 模拟动态产生字母验证码图片功能
Dec 24 Python
django 取消csrf限制的实例
Mar 13 Python
浅析python 通⽤爬⾍和聚焦爬⾍
Sep 28 Python
几款好用的python工具库(小结)
Oct 20 Python
pycharm配置安装autopep8自动规范代码的实现
Mar 02 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
关于尾递归的使用详解
2013/05/02 PHP
实用的简单PHP分页集合包括使用方法
2013/10/21 PHP
php对xml文件的增删改查操作实现方法分析
2017/05/19 PHP
PHP判断一个数组是另一个数组子集的方法详解
2017/07/31 PHP
php7性能提升的原因详解
2019/10/13 PHP
javascrip关于继承的小例子
2013/05/10 Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
2015/03/02 Javascript
如何检测JavaScript的各种类型
2016/07/30 Javascript
Jq通过td获取同行其它列td的方法
2016/10/05 Javascript
jQuery设计思想
2017/03/07 Javascript
js中less常用的方法小结
2017/08/09 Javascript
Vue程序调试的方法
2019/06/17 Javascript
[36:29]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 LGD vs TNC
2018/04/02 DOTA
Python实现批量把SVG格式转成png、pdf格式的代码分享
2014/08/21 Python
Python 探针的实现原理
2016/04/23 Python
python中numpy基础学习及进行数组和矢量计算
2017/02/12 Python
Python 实现王者荣耀中的敏感词过滤示例
2019/01/21 Python
Python3.5模块的定义、导入、优化操作图文详解
2019/04/27 Python
pandas分区间,算频率的实例
2019/07/04 Python
用Python实现二叉树、二叉树非递归遍历及绘制的例子
2019/08/09 Python
Tensorflow之MNIST CNN实现并保存、加载模型
2020/06/17 Python
CSS实现的一闪而过的图片闪光效果
2014/04/23 HTML / CSS
HTML5 创建canvas元素示例代码
2014/06/04 HTML / CSS
美国正宗设计师眼镜在线零售商:EYEZZ
2019/03/23 全球购物
大学生党员自我批评
2014/02/14 职场文书
《最大的“书”》教学反思
2014/02/14 职场文书
环保建议书
2014/03/12 职场文书
租房协议书怎么写
2014/04/10 职场文书
夏季药店促销方案
2014/08/22 职场文书
城市规划应届生推荐信
2014/09/08 职场文书
交通事故一次性赔偿协议书范本
2014/11/02 职场文书
师范生见习报告范文
2014/11/03 职场文书
2014年部门工作总结
2014/11/12 职场文书
优秀团员个人总结
2015/02/26 职场文书
java中用float时,数字后面加f,这样是为什么你知道吗
2021/09/04 Java/Android
HTML基础详解(上)
2021/10/16 HTML / CSS