Redian新闻
>
理解Python中GIL锁

理解Python中GIL锁

公众号新闻

理解Python中GIL锁

GIL(Global Interpreter Lock)不是Python独有的特性,它只是在实现CPython(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.)

由定义可知,GIL是一个互斥锁(mutex)。它阻止了 多个线程同时执行Python字节码,毫无疑问,这降低了执行效率。理解GIL的必要性,需要了解CPython对于线程安全的内存管理机制。

首先,我们来看看单核CPU下,多线程任务是如何调度的。

由图可知,由于GIL的机制,单核CPU在同一时刻只有一个线程在运行。当线程遇到IO操作或Timer Tick到期,释放GIL锁。其他的两个线程去竞争这把锁,得到锁之后,才开始运行。

线程释放GIL锁有两种情况,一是遇到IO操作,二是Time Tick到期。IO操作很好理解,比如发出一个http请求,等待响应。那么Time Tick到期是什么呢?Time Tick规定了线程的最长执行时间,超过时间后自动释放GIL锁。

虽然都是释放GIL锁,但这两种情况是不一样的。比如,Thread1遇到IO操作释放GIL,由Thread2和Thread3来竞争这个GIL锁,Thread1不再参与这次竞争。如果是Thread1因为Time Tick到期释放GIL,那么三个线程可以同时竞争这把GIL锁,可能出现Thread1在竞争中胜出,再次执行的情况。单核CPU下,这种情况不算特别糟糕。因为只有1个CPU,所以CPU的利用率是很高的。

在多核CPU下,由于GIL锁的全局特性,无法发挥多核的特性,GIL锁会使得多线程任务的效率大大降低。

Thread1在CPU1上运行,Thread2在CPU2上运行。GIL是全局的,CPU2上的Thread2需要等待CPU1上的Thread1让出GIL锁,才有可能执行。如果在多次竞争中,Thread1都胜出,Thread2没有得到GIL锁,意味着CPU2一直是闲置的,无法发挥多核的优势。

为了避免同一线程霸占CPU,在python3.x中,线程会自动的调整自己的优先级,使得多线程任务执行效率更高。

既然GIL降低了多核的效率,那保留它的目的是什么呢?这就和线程执行的安全有关。

准确的说,GIL的线程安全是粗粒度的。也就是说,有GIL都不意味着线程安全。比如下面这个例子:

def add():
    global n
    for i in range(10**1000):
        n = n +1
def sub():
    global n
    for i in range(10**1000):
        n = n - 1
n = 0
import threading
a = threading.Thread(target=add,)
b = threading.Thread(target=sub,)
a.start()
b.start()
#join 用于阻塞主线程,避免过早打印n
a.join()
b.join()
print n

上面的程序对n做了同样数量的加法和减法,那么n理论上是0。但运行程序,打印n,发现它不是0。问题出在哪里呢,问题在于python的每行代码不是原子化的操作。比如n = n+1这步,不是一次性执行的。如果去查看python编译后的字节码执行过程,可以看到如下结果。

19 LOAD_GLOBAL              1 (n)
22 LOAD_CONST               3 (1)
25 BINARY_ADD          
26 STORE_GLOBAL             1 (n)

从过程可以看出,n = n +1 操作分成了四步完成。因此,n = n+1不是一个原子化操作。

1.加载全局变量n,2.加载常数1,3.进行二进制加法运算,4.将运算结果存入变量n。

根据前面的线程释放GIL锁原则,线程a执行这四步的过程中,有可能会让出GIL。如果这样,n=n+1的运算过程就被打乱了。最后的结果中,得到一个非零的n也就不足为奇。

这就是为什么我们说GIL是粗粒度的,它只保证了一定程度的安全。如果要做到线程的绝对安全,是不是所有的非IO操作,我们都需要自己再加一把锁呢?答案是否定的。在python中,有些操作是是原子级的,它本身就是一个字节码,GIL无法在执行过程中释放。对于这种原子级的方法操作,我们无需担心它的安全。比如sort方法,[1,4,2].sort(),翻译成字节码就是CALL METHOD 0。只有一行,无法再分,所以它是线程安全的。

总结

对于IO密集型应用,多线程的应用和多进程应用区别不大。即便有GIL存在,由于IO操作会导致GIL释放,其他线程能够获得执行权限。由于多线程的通讯成本低于多进程,因此偏向使用多线程。

对于计算密集型应用,多线程处于绝对劣势,可以采用多进程或协程。

链接:https://zhuanlan.zhihu.com/p/97218985

(版权归原作者所有,侵删)

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
Julia 快到离谱?不,它并没有比Python快340000,000,000倍Python如何修改美女桌面在 VS Code 和 Codium 中编写 Python 程序 | Linux 中国Q526: 如何高效学习 Python 的第三方库?豪赚5W!居家做Python私活,真香!Arduino宣布支持MicroPython3天速成!这份清华大学出品的《Python漫画书》领到就是赚到!在加国金融圈,SQL比Python更靠谱Python的魔术方法小结在 Linux 上试试这个基于 Python 的文件管理器 | Linux 中国阿瑟新剧大火!然而学Python的留学生看了都在骂人深入理解Pytorch中的分布式训练两天赚1w,用Python接私活的一些技巧美国档案--杜威的护照申请表和入境档案速领!清华大学出品《Python教学书》!零基础3天就能速成Acciona Energía 收购德州最大的电池储能项目R语言工作者入门python,你需要实际解决这三个问题ChatGPT发明「史莱姆语」,词汇语法规则全都有,还配了「史翻英」Python代码速领!清华大学出品《Python漫画教学书》!零基础3天就能速成清华大学终于把Python整理成了漫画书…加州圣卡塔利娜岛(Santa Catalina Island),海湾即景10 个杀手级的 Python 自动化脚本!狂揽两千星,速度百倍提升,高性能Python编译器Codon开源SQLAlchemy 2.0.0发布首个RC,Python ORM框架Million Dollar Road 的雪 - 多彩科州之旅(八)import 一个“太极”库,让 Python 代码提速 100 倍!硬核有奖问卷|你选择 JavaScript 还是 Python?下一个热门语言由你定义阿瑟新剧大火!然而学Python的看了都在骂人Q527:理解Python装饰器的3个案例做了盘椒盐虾重新解释E=mc2后的首战---估算夸克速度仅剩一席|三周掌握Python, SQL, R, Excel, VBA等必备工具Python 项目工程化最佳实践指南行业入门|量化分析,一个工作语言是Python和Chinese的行业ChatGPT竟写出毁灭人类计划书,还给出相应Python代码,网友:AI正在指数级发展
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。