返回介绍

建议68:理解 GIL 的局限性

发布于 2024-01-30 22:19:09 字数 2368 浏览 0 评论 0 收藏 0

在Python多线程编程中,你有没有遇到过这种问题:多线程Python程序运行的速度比只有一个线程的时候还要慢?除了程序本身的并行性之外,很大程度上与GIL有关。GIL在Python中是一个很有争议的话题,由于它的存在,多线程编程在Python中似乎并不理想,为什么这么说呢?先来了解一下GIL。GIL被称为为全局解释器锁(Global Interpreter Lock),是Python虚拟机上用作互斥线程的一种机制,它的作用是保证任何情况下虚拟机中只会有一个线程被运行,而其他线程都处于等待GIL锁被释放的状态。对于有I/O操作的多线程,其线程执行状态如图6-6所示。不管是在单核系统还是多核系统中,始终只有一个获得了GIL锁的线程在运行,每次遇到I/O操作便会进行GIL锁的释放。

但如果是纯计算的程序,没有I/O操作,解释器则会根据sys.setcheckinterval的设置来自动进行线程间的切换,默认情况下每隔100个时钟(注:这里的时钟指的是Python的内部时钟,对应于解释器执行的指令)就会释放GIL锁从而轮换到其他线程的执行,示意图如图6-7所示。

图6-6 Python虚拟机中I/O操作中GIL的变换过程

图6-7 无I/O操作时GIL的变换过程

在单核CPU中,GIL对多线程的执行并没有太大影响,因为单核上的多线程本质上就是顺序执行的。但对于多核CPU,多线程并不能真正发挥优势带来效率上明显的提升,甚至在频繁I/O操作的情况下由于存在需要多次释放和申请GIL的情形,效率反而会下降。那么,有人不禁会问:Python解释器中为什么要引入GIL呢?来思考这样一个情形:我们知道Python中对象的管理与引用计数器密切相关,当计数器变为0的时候,该对象便会被垃圾回收器回收。当撤销对一个对象的引用时,Python解释器对对象以及其计数器的管理分为以下两步:

1)使引用计数值减1。

2)判断该计数值是否为0,如果为0,则销毁该对象。

假设线程A和B同时引用同一个对象obj,这时obj的引用计数值为2。如果现在线程A打算撤销对obj的引用。当执行完第一步的时候,由于存在多线程调度机制,A恰好在这个关键点被挂起,而B进入执行状态,如图6-8所示。但不幸的是B也同样做了撤销对obj的引用的动作,并顺利完成了所有两个步骤,这个时候由于obj的引用计数器为0,因此对象被销毁,内存被释放。但如果此时A再次被唤醒去执行第二步操作的时候会发现已经面目全非,则其操作结果完全未知。

图6-8 无GIL存在时线程的同步

鉴于此,在Python解释器中引入了GIL,以保证对虚拟机内部共享资源访问的互斥性。GIL的引入确实使得多线程不能在多核系统中发挥优势,但它也带来了一些好处:大大简化了Python线程中共享资源的管理,在单核CPU上,由于其本质是顺序执行的,一般情况下多线程能够获得较好的性能。此外,对于扩展的C程序的外部调用,即使其不是线程安全的,但由于GIL的存在,线程会阻塞直到外部调用函数返回,线程安全不再是一个问题。

多核CPU已经成为一个常见的现象,GIL的局限性限制了其在多核CPU上发挥优势,因此对于GIL的去留也曾引发过激烈的讨论。Guido以及Python的开发人员都有一个很明确的解释,那就是去掉GIL并不容易。实际上在1999年,针对Python1.5,Greg Stein发布了一个补丁,该补丁中GIL被完全移除,使用高粒度的锁来代替。然而这种解决方案并没有带来理想的效果,多核多线程速度的提升并没有随着核数的增加而线性增长,反而给单线程程序的执行速度带来了一定的代价,当用单线程执行时,速度大约降低了40%。因此,这种方案最终也被放弃。在Python3.2中重新实现了GIL,其实现机制主要集中在两个方面:一方面是使用固定的时间而不是固定数量的操作指令来进行线程的强制切换;另一个方面是在线程释放GIL后,开始等待,直到某个其他线程获取GIL后,再开始去尝试获取GIL,这样虽然可以避免此前获得GIL的线程,不会立即再次获取GIL,但仍然无法保证优先级高的线程优先获取GIL。这种方式只能解决部分问题,并未改变GIL的本质,GIL本质上的改观目前并没有非常明朗的前景。不过也不需要那么悲观,Python提供了其他方式可以绕过GIL的局限,比如使用多进程multiprocessing模块或者采用C语言扩展的方式,以及通过ctypes和C动态库来充分利用物理内核的计算能力。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文