Python并发:多线程与多进程的详解
本篇概要
1.线程与多线程
2.进程与多进程
3.多线程并发下载图⽚
4.多进程并发提⾼数字运算
关于并发
在计算机编程领域,并发编程是⼀个很常见的名词和功能了,其实并发这个理念,最初是源于铁路和电报的早期⼯作。⽐如在同⼀个铁路系统上如何安排多列⽕车,保证每列⽕车的运⾏都不会发⽣冲突。
后来在20世纪60年代,学术界对计算机的并⾏计算开始进⾏研究,再后来,操作系统能够进⾏并发的处理任务,编程语⾔能够为程序实现并发的功能。
横幅广告代码线程与多线程
什么是线程
⼀个线程可以看成是⼀个有序的指令流(完成特定任务的指令),并且可以通过操作系统来调度这些指令流。
线程通常位于进程程⾥⾯,由⼀个程序计数器、⼀个堆栈和⼀组寄存器以及⼀个标识符组成。这些线程是处理器可以分配时间的最⼩执⾏单元。
线程之间是可以共享内存并且互相通信的。但是当两个线程之间开始共享内存,就⽆法保证线程执⾏的顺序,这可能导致程序错误,或者产⽣错误的结果。这个问题我们⽇后会专门提及。
下⾯这个图⽚展⽰了多个线程在多个CPU中的存在⽅式:
线程的类型
在⼀个典型的操作系统⾥⾯,⼀般会有两种类型的线程:
1.⽤户级线程:我们能够创建、运⾏和杀死的线程;
2.内核级线程:操作系统运⾏的低级别线程;
Python⼯作在⽤户级线程上,我们介绍的内容也主要是在⽤户级的线程上运⾏的。
什么是多线程
现在的CPU基本上都是多线程的CPU,⽐如我们随意从京东上⼀个Inter的酷睿i5处理器,看看它的产品规格:
这些CPU能够同时运⾏多个线程来处理任务,其实从本质上来说,这些CPU是利⽤⼀个能够在多个线程之间快速切换的单个内核来完成多线程的运⾏的,切换线程的速度⾜够快,所以我们并不会感觉到。但实质上,它们并不是同时运⾏的。
为了形象的理解多线程,我们来回忆⼀个场景。
在⼤学时代,期末的时候,有些科⽬的⽼师为了不为难⼤家,把考试设为开卷考试,不知道⼤家⾯对开卷考试的时候,做题的顺序是怎样的?
在单线程的⼯作模式下,我们从选择题到填空题到简答题再到分析题,⼀个⼀个按顺序的写。
遇到⼀个特别难的题⽬,我们就要翻书翻资料了,当然既然是开卷考试,有些题⽬的答案就不可能直接出现在教科书中,那么我们就要花费更多的时间来答案,直到考试结束,因为某个难题耗费的翻书时间太多,导致后⾯⼀些简单的题⽬也没⽤做,嗯,开卷都写不完试卷,挂科名额就给你了。
⽽在多线程的⼯作模式下,我们也是按顺序写,但是遇到难题时,我们会稍微从书中答案,如果没到,就先做下⾯的题⽬,把会做的题⽬做好,做好了容易的题⽬,再回到那个难题上,仔细从书中的蛛丝马迹中答案。
在这个例⼦⾥⾯,我们只是⼀个⼈来完成,如果想要更快地完成考试,就得跟其他同学通⼒合作和分⼯了。
让我们看看线程的⼀些优点:
1.多线程能够有效提升I/O阻塞型程序的效率;
2.与进程相⽐,占⽤的系统资源少;
3.线程间能够共享资源,⽅便进⾏通信;
线程还有⼀些缺点:
1.Python中有全局解释器锁(GIL)的限制;
2.虽然线程之间能够进⾏通信,但是容易导致程序结果出错,使⽤的时候必须⼩⼼;
delphi xe2
3.在多线程之间切换的计算代价⾼,会导致程序的整体性能下降。
进程与多进程
进程在本质上与线程⾮常相似,进程⼏乎可以完成线程能够完成的任何事情。
按照上⾯开卷考试的例⼦,如果我们和室友组成⼀个⼩团伙,那么我们就有四个CPU(4个⼈),四个⼈分别写和不同的答案,这样考试的效率会提⾼很多。dubbo线程模型
⼀个进程⾥⾯,包含⼀个主线程,还可以⽣成很多⼦线程,每个线程都包含⾃⼰的寄存器组合堆栈。如果有需要的话,可以将它们组成多线程。
下⾯是单线程单进程和多线程单进程的⽰例:
进程的特性
⼀个进程通常包含以下的内容:
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的进程处理模块multiprocessing,我们可以有效的利⽤机器上所有的处理器,这有助于我们在处理CPU密集型任务时获得更⾼的性能。
使⽤multiprocessing模块,查看我们机器上的CPU核⼼数量:
结果返回⼀个数字,为CPU核⼼数。
多进程不仅能够提⾼我们的计算机的利⽤率,还能够避免全局解释器锁的限制,⼀个潜在的缺点是多进程间不能进⾏共享和通信(可以通过其他⼿段实现),但是这个缺点同时也使多进程更加容易使⽤和避免出现崩溃。
Python的局限性
在⽂章的前⾯,我们谈到了在Python中存在的全局解释器锁GIL的局限性。那GIL到底是个什么东西?
京东python入门教程
GIL本质上是⼀个互斥锁,它可以防⽌多个线程同时执⾏Python代码。它是⼀个只能由⼀个线程保持的锁,如果你想要⼀个线程去执⾏代码,那么在它执⾏代码之前,⾸先必须获得这个锁。这样做的⼀个好处是,当它被锁定的时候,没有别的进程可以同时运⾏代码,⼀定程度上避免了线程间的冲突:
上⾯这个图说明了多个线程如何被GIL阻塞。每个线程必须等待获取到GIL才能进⾏下⼀步的运⾏,然后再释放GIL。线程之间使⽤随机循环的⽅式,所以并不能控制和保证哪个线程会先得到GIL。
这样的设计似乎很反⼈类,⽽这也是很多⼈诟病Python的地⽅。但是,这个设计确实是保证的多线程之间的内存安全。
现在我们已经了解了线程和进程,以及Python的⼀些限制,现在是时候了解⼀下我们如何在应⽤程序中使⽤多线程多进程,以提⾼程序的速度。
并发⽂件下载
毫⽆疑问的,展现多线程优点的⼀个例⼦就是使⽤多线程来下载多个图⽚或者⽂件,由于I/O的阻塞性质,下载任务可能是多线程最佳的运⽤场景了。
我们访问10次,获得10次⽂本⽂件,然后保存在本地。
stagnation如何记忆先看看⼀个普通的爬取:
我们引⼊了模块quest,然后创建了⼀个函数downloadImage()⽤于下载⽂件,创建了⼀个函数main()⽤于对下载函数进⾏遍历20次。
耗时4秒多。
下⾯看看使⽤多线程的:
程序的前部分⼤同⼩异,后⾯我们创建了⼀个threads列表,,然后遍历10次,创建⼀个新的线程对象,将其添加到threads列表中,然后启动该线程。
eskeyword最大长度
最后,我们通过遍历我们的threads列表来调⽤我们的线程,然后调⽤join()⽅法在每个线程上,这确保我们在下载完⽂件之前,不会执⾏剩下的代码。
运⾏代码,可以发现程序⼏乎同时启动了10个下载任务,然后在图⽚下载完成后,再打印出来。
耗时0.1秒,效率提⾼很多。
但是需要注意的是,在⽹络中进⾏⽂件IO,还需要考虑⽹络状况和⾃⾝机器的影响,不同的⽹络状况下,完成的效率也不⼀
样。
并发数字运算
I/O密集型的任务适合于多线程,⽽CPU密集型的任务则适合⽤多进程。
在下⾯的例⼦⾥,我们将出100万个20000到100000000之间随机数的质数。
顺序运算:
耗时18秒。
多进程运算:
耗时11秒。
我们分别按顺序循环100万遍和使⽤多进程的进程池循环100万次,多进程模式下速度提升了近7秒。
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,谢谢⼤家对的⽀持。如果你想了解更多相关内容请查看下⾯相关链接

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。