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 解析XML python模块xml.dom解析xml实例代码
Feb 07 Python
Python实现短网址ShortUrl的Hash运算实例讲解
Aug 10 Python
python实现简易版计算器
Jun 22 Python
python实现两张图片的像素融合
Feb 23 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
May 18 Python
Python实现将字符串的首字母变为大写,其余都变为小写的方法
Jun 11 Python
使用python实现ftp的文件读写方法
Jul 02 Python
Window10下python3.7 安装与卸载教程图解
Sep 30 Python
TensorFlow tf.nn.conv2d实现卷积的方式
Jan 03 Python
Pycharm 2020最新永久激活码(附最新激活码和插件)
Sep 17 Python
MAC平台基于Python Appium环境搭建过程图解
Aug 13 Python
利用Python实现自动扫雷小脚本
Dec 17 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
用 PHP5 轻松解析 XML
2006/12/04 PHP
php mssql 时间格式问题
2009/01/13 PHP
php正则表达匹配中文问题分析小结
2012/03/25 PHP
PHP 利用AJAX获取网页并输出的实现代码(Zjmainstay)
2012/08/31 PHP
PHP中CURL的CURLOPT_POSTFIELDS参数使用细节
2014/03/17 PHP
PHP魔术方法__GET、__SET使用实例
2014/11/25 PHP
php自动给网址加上链接的方法
2015/06/02 PHP
PHP实现表单提交时去除斜杠的方法
2016/12/26 PHP
PHP自动生成缩略图函数的源码示例
2019/03/18 PHP
Laravel timestamps 设置为unix时间戳的方法
2019/10/11 PHP
php操作redis常见方法示例【key与value操作】
2020/04/14 PHP
php并发加锁问题分析与设计代码实例讲解
2021/02/26 PHP
原型方法的不同写法居然会影响调试的解决方法
2007/03/08 Javascript
Web开发者必备的12款超赞jQuery插件
2010/12/03 Javascript
异步JS框架的作用以及实现方法
2015/10/29 Javascript
省市二级联动小案例讲解
2016/07/24 Javascript
JS拖动选择table里的单元格完整实例【基于jQuery】
2019/05/28 jQuery
vue+element 模态框表格形式的可编辑表单实现
2019/06/07 Javascript
使用JavaScrip模拟实现仿京东搜索框功能
2019/10/16 Javascript
Vue 中 a标签上href无法跳转的解决方式
2019/11/12 Javascript
bootstrap-paginator服务器端分页使用方法详解
2020/02/13 Javascript
ES6中Promise的使用方法实例总结
2020/02/18 Javascript
vue created钩子函数与mounted钩子函数的用法区别
2020/11/05 Javascript
python采用requests库模拟登录和抓取数据的简单示例
2014/07/05 Python
Python 新建文件夹与复制文件夹内所有内容的方法
2018/10/27 Python
python程序封装为win32服务的方法
2021/03/07 Python
简单了解python的一些位运算技巧
2019/07/13 Python
pandas中read_csv的缺失值处理方式
2019/12/19 Python
Python中使用filter过滤列表的一个小技巧分享
2020/05/02 Python
DHC中国官方购物网站:日本通信销售No.1化妆品
2016/08/20 全球购物
Under Armour安德玛法国官网:美国高端运动科技品牌
2018/06/29 全球购物
给老婆的搞笑检讨书
2014/01/12 职场文书
《油菜花开了》教学反思
2014/02/22 职场文书
青年岗位能手事迹材料
2014/12/23 职场文书
常住证明范本
2015/06/23 职场文书
企业内部管理控制:银行存款控制制度范本
2020/01/10 职场文书