23 | 你真的懂Python GIL(全局解释器锁)吗?
景霄
该思维导图由 AI 生成,仅供参考
你好,我是景霄。
前面几节课,我们学习了 Python 的并发编程特性,也了解了多线程编程。事实上,Python 多线程另一个很重要的话题——GIL(Global Interpreter Lock,即全局解释器锁)却鲜有人知,甚至连很多 Python“老司机”都觉得 GIL 就是一个谜。今天我就来为你解谜,带你一起来看 GIL。
一个不解之谜
耳听为虚,眼见为实。我们不妨先来看一个例子,让你感受下 GIL 为什么会让人不明所以。
比如下面这段很简单的 cpu-bound 代码:
现在,假设一个很大的数字 n = 100000000,我们先来试试单线程的情况下执行 CountDown(n)。在我手上这台号称 8 核的 MacBook 上执行后,我发现它的耗时为 5.4s。
这时,我们想要用多线程来加速,比如下面这几行操作:
我又在同一台机器上跑了一下,结果发现,这不仅没有得到速度的提升,反而让运行变慢,总共花了 9.6s。
我还是不死心,决定使用四个线程再试一次,结果发现运行时间还是 9.8s,和 2 个线程的结果几乎一样。
这是怎么回事呢?难道是我买了假的 MacBook 吗?你可以先自己思考一下这个问题,也可以在自己电脑上测试一下。我当然也要自我反思一下,并且提出了下面两个猜想。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
Python GIL(全局解释器锁)对Python并发编程产生重要影响。本文通过实例展示了GIL的影响,解释了GIL的作用和原因。GIL是CPython解释器中的全局解释器锁,阻止多个Python线程同时执行,导致线程并不像期望的那样并行。文章深入浅出地介绍了Python GIL的工作原理,以及为什么Python的线程仍需要考虑线程安全。尽管GIL限制了多线程并行执行,但并不意味着程序就可以高枕无忧,仍需要注意线程安全。文章提供了绕过GIL的两种思路,强调在大部分情况下并不需要过多考虑GIL。最后,留下了两道思考题,引发读者对GIL的思考和讨论。 文章通过实例和技术细节深入浅出地介绍了Python GIL,对于想要深入了解Python并发编程的读者具有很高的参考价值。同时,提到了绕过GIL的两种思路,为读者提供了解决GIL限制的方法。文章还留下了两道思考题,引发读者对GIL的思考和讨论。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Python 核心技术与实战》,新⼈⾸单¥59
《Python 核心技术与实战》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(36)
- 最新
- 精选
- 建强思考题1: 由于GIL采用轮流运行线程的机制,GIL需要在线程之间不断轮流进行切换,线程如果较多或运行时间较长,切换带来的性能损失可能会超过单线程。 思考题2: 个人觉得GIL仍然是一种好的设计,虽然损失了一些性能,但在保证资源不发生冲突,预防死锁方面还是有一定作用的。 以上是个人的一点肤浅理解,请老师指正。
作者回复: 讲的不错
2019-10-25213 - jackstraw关于绕过GIL的第二个方式:将关键的性能代码放到别的语言中(通常C++)实现;这种解决方式指的是在别的语言中使用多线程的方式处理任务么?就是不用python的多线程,而是在别的语言中使用多线程?
作者回复: 对
2020-01-159 - 小侠龙旋风先mark一下学到的知识点: 一、查看引用计数的方法:sys.getrefcount(a) 二、CPython引进GIL的主要原因是: 1. 设计者为了规避类似内存管理这样的复杂竞争风险问题(race condition); 2. CPython大量使用C语言库,但大部分C语言库都不是线程安全的(线程安全会降低性能和增加复杂度)。 三、绕过GIL的两种思路: 1. 绕过CPython,使用JPython等别的实现; 2. 把关键性能代码放到其他语言中实现,比如C++。 问答老师的问题: 1. cpu-bound属于计算密集型程序,用多线程运行时,每个线程在开始执行时都会锁住GIL、执行完会释放GIL,这两个步骤比较费时。相比单线程就没有切换线程的问题,所以更快。 相反,在处理多阻塞高延迟的IO密集型程序时,因为多线程有check interval机制,若遇阻塞,CPython会强制当前线程让出(释放)GIL,给其他线程执行的机会。所以能提高程序的执行效率。 2. 第二个问题摘抄了知乎上的讨论: 在python3中,GIL不使用ticks计数,改为使用计时器(执行时间达到阈值后interval=15毫秒,当前线程释放GIL),这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。多核多线程比单核多线程更差,原因是单核下多线程,每次释放GIL,唤醒的那个线程都能获取到GIL锁,所以能够无缝执行,但多核下,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。 经常会听到老手说:“python下想要充分利用多核CPU,就用多进程”,原因是什么呢?原因是:每个进程有各自独立的GIL,互不干扰,这样就可以真正意义上的并行执行,所以在python中,多进程的执行效率优于多线程(仅仅针对多核CPU而言)。所以我们能够得出结论:多核下,想做并行提升效率,比较通用的方法是使用多进程,能够有效提高执行效率。2019-07-062144
- leixin有重要的一点没讲,GIL会在遇到io的时候自动释放,给其他线程执行的机会,这样Python多线程在io阻塞的多任务中有效。2019-07-01341
- leixin老师,我曾经去某大厂面试。人家问了我几个问题,比说说,你知道元类吗?Python是如何解决循环引用的?换句话说,Python的垃圾回收机制是如何?我后来自己找了些资料看了,还是,不是理解的特别明白。老师后面的课程能帮我们讲解下吗?2019-07-01134
- helloworldpython的单线程和多线程同时都只能利用一颗cpu核心,对于纯cpu heavy任务场景,不涉及到io耗时环节,cpu都是充分利用的,多线程和单线程相比反倒是多了线程切换的成本,所以性能反而不如单线程。2019-07-0110
- SCAR1.cpu-bound任务的多线程相比单线程,时间的增加在于锁添加的获取和释放的开销结果。 2.返回到python诞生的年代,GIL相对来说是合理而且有效率的,它易于实现,很容易就添加到python中,而且它为单线程程序提供了性能提升。以至于Guido在“It isn't Easy to Remove the GIL”里面说“ I'd welcome a set of patches into Py3k only if the performance for a single-threaded program (and for a multi-threaded but I/O-bound program) does not decrease”。而到现在为止,任何尝试都没有达到这一条件。2019-07-0118
- farFlight另外,在测试不加锁的 foo 函数的时候,我这里循环测试10000次也不会见到n!=100的情况,这是为什么呢?2019-07-0124
- 张巡这是mackbook被黑的最惨的一次,哈哈哈2020-09-133
- Kfreer1在我们处理 cpu-bound 的任务(文中第一个例子)时,为什么有时候使用多线程会比单线程还要慢些? 答:由于CPython中GIL的存在(运行线程前需要先获取GIL),所以即便是多线程运行,同一时刻也只能有一个线程处于运行状态,且切线程之间切换时还要消耗一部分资源。这就导致cpu密集型任务下多线程反而没有单线程运行的快。 2你觉得 GIL 是一个好的设计吗?事实上,在 Python 3 之后,确实有很多关于 GIL 改进甚至是取消的讨论,你的看法是什么呢?你在平常工作中有被 GIL 困扰过的场景吗? 答:不是一个好的设计,仅仅是简化了解释器的设计,且并没有解决线程安全的问题。 总结:CPU密集型任务用多进程并行处理(CPU需多核),I/O密集型任务用协程,理论上协程比多线程的效率还要高。2020-05-262
收起评论