文章主要从以下几点总结:
- Python的多线程是怎么工作的
- Python的协程是如何工作的
- Python的多线程与协程的区别
一、Python的多线程是如何工作的
在现行的多核cpu的计算机架构下python多线程并不能充分发挥多线程的并行效果。
下面是一个场景:一台拥有两核CPU的计算机分别运行一个拥有2个线程的python程序和一个同样拥有2个线程的非python程序会发生什么事情呢?假设这2个线程都是进行纯cpu计算的的任务,不会发生阻塞。假设完成一个任务的时间是1s。
非Python程序是可以做到将这2个线程合理分配到2个核去进行计算的,这2个线程实际上就是并行执行的,这样一来完成这2个任务的时间就是1s。
那么这种场景下,Python程序是如何工作的呢?因为GIL(全局解释器锁,当然这把锁并不是所有python解释器特有的,局限于CPython)的原因,python每一个线程在执行的时候都会上这把锁,为什么要上GIL?
关于GIL锁官方有个解释:防止多线程同时执行Python的字节码。
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
上述场景下,python T1线程获取GIL锁,分配到cpu其中一个核里面执行,T2因为没有获得GIL锁,处于就绪阻塞态,等到执行的字节码到了指定的某个值或者T1执行结束,T1才会释放GIL锁,T2才有机会获取。这时上下文进行切换,线程T2才得以执行。
什么是上下文切换呢?操作系统从T1切换到T2的时候,是终止了T1,那么当下一次切回到T1时,怎么知道上一次T1运行到什么位置了呢?所以所以上下文切换的过程要保存T1的执行环境信息,同时也要加载T2线程执行需要的环境信息,这就是上下文切换
上下文切换也是要时间的,这样一来这两个任务完成的时间有可能就是大于2s了,因为两个线程的频繁切换消耗了珍贵的运算时间。
从上述Python多线程的调度过程可以看出,python的多线程没有达到并行而只能达到并发的效果,无论CPU多少核,同一时刻只有一个核心去执行一个线程?
那么有人就会有疑问?上述的计算密集型的程序,由单线程顺序执行不就完了吗?时间就是2s,省去了切换的时间,还要什么Python多线程,Python多线程有什么用?当然有用,当你的程序是io密集型的呢?当一个线程T1进行io时,阻塞了,难道你的cpu在等着吗?这简直就是暴殄天物啊!这时,操作系统可以在这段空闲时间里调度另外一个线程去工作,直到T1准备完毕之后,才有可能再被执行,这样也可以达到提高程序的工作效率的目的。
二、Python的协程是如何工作的
上面提到Python的多线程遇到io阻塞时,会进行上下文切换,达到并发的效果。但是频繁的上下文切换代价是很高的,那么如何使并发的效果更进一步呢?减少上下文切换开销估计是首先想到的,于是出现了协程,这里不是说协程的概念诞生的时间迟。
那么Python协程是如何工作的呢?
协程其实是用户态的线程,也叫微线程,它的切换调度是发生在线程里,也就是由线程负责的,负责保存协程切换的执行环境信息,而前文提到的线程是由操作系统切换调度的。也就是说,假设分配给某个线程的时间片,这个线程可以自由决定将这个时间片分配给它下面的协程。
一般情况下,怎么就说python里1个线程里的2个协程切换就比2个线程的上下文切换效率更好呢?
总结来说:协程的切换只是在用户空间进行切换操作,不涉及内核操作,其切换速度远快于线程,具体的话也没有很深入的了解,请参考为什么协程切换的代价比线程切换低?这也是我觉得线程与协程的最大区别所在
三、Python多线程与协程的区别
上文提到,在多核cpu的基础下,Python多线程并没有发挥并行的威力,只是达到并发的效果,于是我认为它们的区别在于:
- 并发的效果会更好,这是源于协程的切换开销代价对于线程来说更小的事实
- 协程是用户态线程,由线程进行调度,而线程则是由操作系统来进行调度,这就给可操纵留有可能的余地,毕竟协程更加灵活
后记
总结还是比较粗糙,主要还是为了方便自己的复盘知识体系。当然如果有帮到跟我以前一样懵逼的人当然更好。如有不恰当之处,欢迎指出补充,学习要一直走在不断修正的路上才能更好
网络世界纷扰万分,我也不敢苟同,每一篇关于Python协程线程的文章都对,但是自己理解为上,本文也借鉴了很多人的文章总结的,个人感觉知乎:一亩三分地写得很棒。