Python GIL

CPython中(注意,在Python 的其他实现中,不存在 GIL机制),Global Interpreter Lock,或者 GIL,是一个互斥锁(mutex),可以保护对 Python 对象的访问,防止多个线程同时执行 Python 字节码(bytecodes)。 GIL 防止 race conditions 并确保线程安全(thread-safy)。GIL 确保在任何时候都只有一个线程在运行。 因为一次只能运行一个线程,所以不能使用多线程编程。简言之,这个互斥锁是必要的,主要是因为 CPython 的内存管理不是线程安全的。

CPython:根据 Wikipedia,“编程语言是用于编写程序的符号,是计算或算法的规范”。 这意味着它只是编写代码的规则和语法。 另外,我们有一个编程语言实现,在大多数情况下,它是实际的解释器或编译器。
Python是一种语言。 CPython 是 Python 在 C 中的实现。Jython 是 Java 中的实现等。

bytecode:字节码的名称源于具有一字节操作码后跟可选参数的指令集。 诸如字节码之类的中间表示可以由编程语言实现输出以简化解释,或者它可以用于通过允许相同的代码在不同设备上跨平台运行来减少对硬件和操作系统的依赖。 字节码通常可以直接在虚拟机(p-code 机器,即解释器)上执行,也可以进一步编译成机器码以获得更好的性能。

线程-安全

线程安全代码仅以不干扰其他线程的方式操作共享数据。

race condition 的一个例子

首先,创建一个变量:

a = 2

现在假设我们有两个线程,thread_one 和 thread_two。 它们执行以下操作:

  • thread_one :a = a+2
  • thread_two :a = a*3.

如果 thread_one 先访问, thread_two 再访问,结果将是:

  • a = 2 + 2,
  • .a = 4 * 3

但是,如果发生 thread_two 先运行,然后是 thread_one,我们会得到不同的输出:

  • a = 2 * 3
  • a = 6 + 2

所以执行顺序显然对输出很重要。 不过,还有更糟糕的可能结果! 如果两个线程同时读取变量 a ,做他们的事情,然后分配新值怎么办? 他们都会看到 a = 2。取决于谁先写出结果,a 最终会是 4 或 6。这不是我们预期的! 这就是我们所说的 race condition。

Race condition:系统行为取决于其他不可控事件的顺序或时间的系统状况。
Race condition 很难发现,尤其是对于不熟悉这些问题的软件工程师。 此外,它们往往是随机发生的,导致不稳定和不可预测的行为。 众所周知,这些错误很难找到和调试。 这正是 Python 拥有 GIL 的原因——让大多数 Python 用户的生活更轻松。

GIL 解决了什么问题

Python 使用引用计数(reference counting)进行内存管理(memory management)。 这意味着在 Python 中创建的对象有一个引用计数变量,该变量跟踪指向该对象的引用数量。 当这个计数达到零时,对象占用的内存被释放。
让我们看一个简短的代码示例来演示引用计数是如何工作的:

import sys
a = []
b = a
print(sys.getrefcount(a))

输出为

3

在上面的示例中,空列表对象 [] 的引用计数为 3。列表对象由 a、b 和传递给 sys.getrefcount()的参数引用。

让我们重新聚焦到 GIL
问题是这个引用计数变量需要保护免受两个线程同时增加或减少其值的 race condition。如果发生这种情况,可能会导致内存泄漏(leaked memory)而永远不会释放,或者更糟糕的是,在对该对象的引用仍然存在时错误地释放内存。这可能会导致 Python 程序崩溃或其他“奇怪”的错误。

这个引用计数变量可以通过向所有线程共享的数据结构添加锁来保持安全,这样它们就不会被不一致地修改。

但是为每个对象或对象组添加一个锁意味着将存在多个锁,这可能会导致另一个问题——死锁(死锁只有在有多个锁时才会发生)。另一个副作用是由于重复获取和释放锁而导致性能下降。

GIL 是解释器(interpreter)本身的单个锁,它添加了一条规则,即执行任何 Python 字节码都需要获取解释器锁。这可以防止死锁(因为只有一个锁)并且不会引入太多的性能开销。但它有效地使任何受 CPU 限制的 Python 程序成为单线程的。

GIL 虽然被解释器用于 Ruby 等其他语言的解释器,但并不是解决这个问题的唯一方法。某些语言通过使用引用计数以外的方法(garbage collection,垃圾收集)来避免 GIL 对线程安全内存管理的要求。

另一方面,这意味着这些语言通常必须通过添加其他性能提升功能(如 JIT 编译器)来弥补 GIL 单线程性能优势的损失。

参考

https://wiki.python.org/moin/GlobalInterpreterLock
[The Python GIL (Global Interpreter Lock)](https://python.land/python-concurrency/the-
python-gil)
Python vs Cpython
bytecode

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

培之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值