美文网首页
Python 的GIL与线程锁

Python 的GIL与线程锁

作者: UULU | 来源:发表于2019-08-21 16:34 被阅读0次

    Python 程序是如何运行的?

    1. 将源码*.py 编译成字节码*.pyc
    2. 再由 Python 虚拟机逐行解释*.pyc成机器码,通知 CPU 执行

    名词解释:

    • 源码 (Programing Code) - 我们用高级语言敲的代码
    • 字节码 (Byte Code) - 将高级语言编译成更低一级的中间代码,方便虚拟机(VM)执行
    • 机器码 (Machine Code) - 用于操控 CPU 运行的二进制指令代码

    什么是 GIL?

    Python 虚拟机默认使用的是 CPython 解释器(C 语言实现),CPython 使用了 GIL (Golbal Iterpreter Lock - 全局解释器锁),来确保同一时间只有一个线程运行,所以即使再多的线程也只能有效的使用一个 CPU。

    为什么不删除 GIL?

    实验证明,如果放弃 GIL,使用大量细粒度的锁代替,导致单线程性能下降至少 30%。所以说 GIL 在支持多线程的同时能把单线程的优势最大地发挥出来。

    GIL 的限制

    由于 GIL 的存在,即便是多线程的 Python 程序也无法利用多核 CPU 的优势。不过 I/O 密集型程序,依然适合使用多线程,因为它们大部分时间都在等待 I/O,对 CPU 依赖很低。

    名词解释:

    • CPU 密集型 - 数学计算、搜索、图像处理等
    • I/O 密集型 - 文件操作、网络交互、数据库操作等

    怎么充分的利用多核?

    使用多进程替代多线程,或者使用 C 扩展

    有了 GIL 还需要线程锁吗?

    需要。GIL 只是解释器级别的锁,它能保证当多个线程在修改一个变量时不会崩溃,但结果可能是错乱的。

    如多个线程修改全部变量i = i + 1,是先读取 i,再加 1 后,最后写回内存,这会把在读与写期间其他线程对 i 的所有修改覆都盖掉。所以我们需要用线程锁来对整个读写过程加锁。

    PyPy 是什么?

    Python 语言实现的另一种解释器,使用了 JIT 编译方式,没有 GIL。运行 Pure Python (纯 Python 代码) 程序速度更快。如果依赖了 C 扩展,反而会慢,如 MysqlDB、protobuf 库。

    JIT 与其他编译方式的对比:

    • Compilation: 先编译成适用于本机识别的机器码,再运行,不同系统或 CPU 需要重新编译 (如 C/C++)
    • Interpretation: 解释器在程序运行时把字节码逐行解释成机器码,边解释,边执行 (如 CPython/Ruby)
    • JIT Compilation - 及时编译 (Just-in-Time),是对前两张方法的整合,将多次调用的字节码,解释成机器码,缓存起来,优先读缓存,而非解释。支持跨平台的同时,又降低了动态编译对性能的影响。 (如 Java/C#/Pypy)

    总结

    根据应用场景选择最佳的性能优化方案

    1. I/O 密集型: 使用多线程
    2. I/O 密集型 & Pure Python: 使用 Pypy 解释器
    3. CPU 密集型: 使用多进程或把复杂逻辑用 C 扩展实现

    相关文章

      网友评论

          本文标题:Python 的GIL与线程锁

          本文链接:https://www.haomeiwen.com/subject/pldisctx.html