`
san_yun
  • 浏览: 2663957 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

python 线程,GIL 和 ctypes

 
阅读更多

GIL 的全程为 Global Interpreter Lock ,意即 全局解释器锁 在 Python 语言的主流 CPython 实现中,GIL 是一个货真价实的全局线程锁,在解释器解释执行任何 Python 代码时,都需要先获得这把锁才行,解释器在遗到 I/O 操作时会释放这把锁。如果是计算的程序,没有I/O操作,解释器会每隔100次操作就释放这把锁,让别的线程有机会执行(这个次数可以通过 sys.setcheckinterval来调整)。所以,虽然 CPython 的线程库直接封装操作系统的原生线程,但 CPython 进程做为一个整体,同一时间只会有一个获得了 GIL 的线程在跑,其它的线程都处于等待状态等着 GIL 的释放。这也就解释了我们上面的实验结果:虽然有两个死循环的线程,而且有两个物理 CPU 内核,但因为 GIL 的限制,两个线程只是做着分时切换,总的 CPU 占用率还略低于 50%。

 

看起来 python 很不给力啊。GIL 直接导致 CPython 不能利用物理多核的性能加速运算。那为什么会有这样的设计呢?我想,应该还是历史遗留问题。多核 CPU 在 1990 年代还属于类科幻,Guido van Rossum 在创造 python 的时候,也想不到他的语言有一天会被用到很可能 1000+ 个核的 CPU 上面,一个全局锁搞定多线程安全在那个时代应该是最简单经济的设计了。简单而又能满足需求,那就是合适的设计(对设计来说,应该只有合适与否,而没有好与 不好)。怪只怪硬件的发展实在太快了,摩尔定律给软件业的红利这么快就要到头了。短短20年不到,代码工人就不能指望仅仅靠升级 CPU 就能让老软件跑的更快了。在多核时代,编程的免费午餐没有了。如果程序不能用并发挤干每个核的运算性能,那就意谓着会被淘汰。对软件如此,对语言也是一 样。那 Python 对此的策略呢?

 

Python 的应对很简单,以不变应万变。在最新的 python 3 中依然有 GIL。之所以不去掉,原因嘛,不外以下几点:

  • 欲练神功,挥刀自宫:CPython 的 GIL 本意是用来保护所有全局的解释器和环境状态变量的。如果去掉 GIL,就需要多个更细粒度的锁对解释器的众多全局状态进行保护。或者采用 Lock-Free 算法。无论哪一种,要做到多线程安全都会比单使用 GIL 一个锁要难的多。而且改动的对象还是有 20 年历史的 CPython 代码树,更不论有这么多第三方的扩展也在依赖 GIL。对 Python 社区来说,这不异于挥刀自宫,重新来过。
  • 就算自宫,也未必成功:有位牛人曾经做了一个验证用的 CPython,将 GIL 去掉,加入了更多的细粒度锁。但是经过实际的测试,对单线程程序来说,这个版本有很大的性能下降,只有在利用的物理 CPU 超过一定数目后,才会比 GIL 版本的性能好。这也难怪。单线程本来就不需要什么锁。单就锁管理本身来说,锁 GIL 这个粗粒度的锁肯定比管理众多细粒度的锁要快的多。而现在绝大部分的 python 程序都是单线程的。再者,从需求来说,使用 python 绝不是因为看中它的运算性能。就算能利用多核,它的性能也远不可能和 C/C++ 比肩。费了大力气把 GIL 拿掉,反而让大部分的程序都变慢了,这不是南辕北辙吗。
  • 难道 Python 这么优秀的语言真的仅仅因为改动困难和意义不大就放弃多核时代了吗?其实,不做改动最最重要的原因还在于:不用自宫,也一样能成功!

 

除了切掉 GIL 外,果然还有方法让 Python 在多核时代活的滋润?让我们回到本文最初的那个问题:如何能让这个死循环的 Python 脚本在双核机器上占用 100% 的 CPU?其实最简单的答案应该是:运行两个 python 死循环的程序,也就是说,用两个分别占满一个 CPU 内核的 python 进程来做到。确实,多进程也是利用多个 CPU 的好方法。只是进程间内存地址空间独立,互相协同通信要比多线程麻烦很多。有感于此,Python 在 2.6 里新引入了 multiprocessing 这个多进程标准库,让多进程的 python 程序编写简化到类似多线程的程度,大大减轻了 GIL 带来的不能利用多核的尴尬。

 

 

分享到:
评论

相关推荐

    Python中GIL的使用详解

    在Python中,通过使用multiprocessing库,可以轻松地创建多进程程序,这与多线程编程类似,但是每个进程拥有自己的Python解释器和内存空间,因此不受GIL的限制。 在实际应用中,Python的一些IO密集型操作如网络通信...

    python杀死一个线程的方法

    总的来说,Python线程的优雅退出通常依赖于线程自身的合作,即线程定期检查一个停止标志。对于那些无法通过协作停止的线程,可能需要更复杂的同步机制或使用其他并发模型,如`multiprocessing`模块,它提供了更细...

    Python多线程的退出控制实现

    首先,我们要理解Python线程的基本概念。线程是程序执行的最小单元,每个线程都有自己的栈空间,共享全局变量。在Python中,我们可以使用`threading`模块创建和管理线程。然而,由于GIL(全局解释器锁)的存在,...

    python中threading开启关闭线程操作

    然而,需要注意的是,由于GIL(全局解释器锁)的存在,Python的多线程并不能实现真正的并行计算,而是交替执行。对于计算密集型任务,可能需要考虑使用`multiprocessing`模块或者其他多进程库来实现并行处理。

    内嵌python3.7的工具 tool-python

    Python有一个全局解释器锁(GIL),这限制了在同一时刻只有一个线程执行Python字节码。因此,需要确保正确管理线程,避免并发执行Python代码。 5. **异常处理**: C++和Python都有各自的异常处理机制。当Python代码...

    Python 多线程,threading模块,创建子线程的两种方式示例

    总结来说,Python的多线程编程需要考虑到GIL带来的影响,并根据实际应用场景选择合适的方式来创建和管理子线程。通过`threading`模块,可以方便地实现线程的创建和任务的并发执行,从而提升程序效率。同时,理解多...

    python题库.zip

    11. **多线程与多进程**:理解Python的GIL(全局解释器锁),了解threading和multiprocessing模块,用于实现并发和并行计算。 12. **数据分析与可视化**:使用pandas库进行数据清洗、处理和分析,matplotlib和...

    Python3.7.2中文文档-Python教程-合集

    11. **并发编程**:Python提供了线程(threading模块)和进程(multiprocessing模块)来实现并发,以及异步I/O模型如asyncio。 12. **单元测试**:Python的unittest模块用于编写和执行单元测试,确保代码的质量和...

    yara-ctypes:适用于libyara的Python ctypes包

    ctypes在系统函数调用中释放GIL ...将PC发挥其真正潜能。 通过将高级逻辑(例如管理规则路径,过滤路径,控制池执行等)保留在Python之类的语言中,极大地简化了事情。 不再构建PyC扩展... 我发现了一些错误和...

    python使用ctypes调用扩展模块的实例方法

    Python中的ctypes库是用于调用C语言编写的动态链接库(DLL)或共享对象(SO)的关键工具,使得Python程序能够与C代码交互,从而提高执行效率并解决全局解释器锁(GIL)带来的多线程限制。GIL使得Python在多线程环境...

    Python并行数值计算

    然而,Python的标准线程模型受到全局解释器锁(Global Interpreter Lock,简称GIL)的限制,无法在多核处理器上实现真正的并行处理。因此,开发人员需要寻找其他方法来突破这一限制,充分利用多核CPU的性能优势。 #...

    在python中实现强制关闭线程的示例

    首先,理解Python线程的基本概念。Python的`threading`模块提供了创建和管理线程的功能。线程通常有自己的执行栈,可以独立于主程序或其他线程运行。然而,Python的全局解释器锁(GIL)使得在同一时刻只有一个线程能...

    Ubuntu系统中Python无缝调用C程序的实现.zip

    由于GIL(全局解释器锁)的存在,Python的多线程并不能充分利用多核优势。此时,可以使用`multiprocessing`模块的进程池,或者使用异步I/O模型如`asyncio`来实现并发调用C程序。 六、性能优化 在某些场景下,...

    python自学资料进阶

    7. 并发与多线程:探讨Python的并发模型,如线程、进程和GIL(全局解释器锁),并讲解如何使用线程池和异步IO。 8. 网络编程:包括TCP/IP通信、套接字编程,以及HTTP协议的实现,可能还会涉及Web服务器的构建。 9....

    Python3.x 教程

    Python的并发可以通过线程和进程实现,虽然GIL(全局解释器锁)限制了多线程的并行性能,但可以通过进程或者异步IO(如asyncio库)实现高效的并发处理。 十二、Python与其他语言的交互 Python可以通过ctypes、...

    Python学习手册(第3版)_python_

    13. **并发编程**:介绍线程和进程的概念,以及Python中的多线程、多进程,以及GIL(全局解释器锁)对Python并行性能的影响。 14. **Python与其他语言交互**:如C语言扩展、Jython、IronPython等,以及使用ctypes库...

    深入Python3中文版 加 源码

    - 并发模型:GIL(全局解释器锁)的理解,以及如何在Python中实现并发和并行。 - 异步I/O:asyncio模块的异步编程,协程的使用。 5. **内存管理** - Python内存管理机制:垃圾回收、引用计数和可达性分析。 - ...

    Python高性能编程技术

    7. **多线程与多进程**: Python的全局解释器锁(GIL)限制了多线程在CPU密集型任务中的并行性,但在IO密集型任务中,多线程依然有用。多进程可以绕过GIL,利用多核CPU资源,但有额外的进程管理开销。 8. **并发与...

    《Python从入门到精通》ppt课件22章.zip

    15. **并发与多线程**:理解Python的GIL(全局解释器锁),并介绍多线程和多进程的使用。 16. **Python与其他语言的交互**:介绍Python调用C/C++代码的机制,如使用ctypes库。 17. **测试驱动开发**:讲解单元测试...

Global site tag (gtag.js) - Google Analytics