📜  什么是Python全局解释器锁 (GIL)

📅  最后修改于: 2022-05-13 01:54:25.631000             🧑  作者: Mango

什么是Python全局解释器锁 (GIL)

Python Global Interpreter Lock (GIL) 是一种进程锁, Python在处理进程时使用它。通常, Python只使用一个线程来执行一组书面语句。这意味着在Python中一次只会执行一个线程。在PythonPython的 GIL。我们无法在Python中实现多线程,因为我们有全局解释器锁,它限制线程并作为单线程工作。

GIL 为Python解决了什么问题:

Python有一些其他语言没有的东西,那就是引用计数器。在引用计数器的帮助下,我们可以计算在Python内部进行的引用总数,以将值分配给数据对象。由于这个计数器,我们可以对引用进行计数,当这个计数达到零时,变量或数据对象将自动释放。例如

# Python program showing
# use of reference counter
import sys
  
geek_var = "Geek"
print(sys.getrefcount(geek_var))
  
string_gfg = geek_var
print(sys.getrefcount(string_gfg))

输出:

4
5

这个引用计数器变量需要被保护,因为有时两个线程同时增加或减少它的值,这样做可能会导致内存泄漏,所以为了保护线程,我们向所有线程共享的数据结构添加锁,但有时通过添加锁存在多个锁,这导致另一个问题是死锁。为了避免内存泄漏和死锁问题,我们在解释器上使用了单锁,即全局解释器锁(GIL)。为什么选择 GIL 作为解决方案:
Python在后端支持 C 语言, Python拥有的所有相关库大多是用 C 和 C++ 编写的。由于 GIL, Python提供了一种更好的方式来处理线程安全的内存管理。全局解释器锁在Python中很容易实现,因为它只需要为一个线程提供一个锁以在Python中进行处理。 GIL 易于实现,并且很容易添加到Python中。它为单线程程序提供了性能提升,因为只需要管理一个锁。对多线程Python程序的影响:
当用户编写Python程序或任何计算机程序时,在性能上受 CPU 限制的程序与受 I/O 限制的程序之间存在差异。 CPU 通过同时执行许多操作将程序推向极限,而 I/O 程序必须花时间等待输入/输出。例如
代码 1:执行简单倒计时的 CPU 绑定程序

# Python program showing
# CPU bound program
  
import time
from threading import Thread
  
COUNT = 50000000
  
def countdown(n):
    while n>0:
        n -= 1
  
start = time.time()
countdown(COUNT)
end = time.time()
  
print('Time taken in seconds -', end - start)

输出:

Time taken in seconds - 2.5236213207244873

代码 2:两个线程并行运行

# Python program showing
# two threads running parallel
  
import time
from threading import Thread
  
COUNT = 50000000
  
def countdown(n):
    while n>0:
        n -= 1
  
t1 = Thread(target = countdown, args =(COUNT//2, ))
t2 = Thread(target = countdown, args =(COUNT//2, ))
  
start = time.time()
t1.start()
t2.start()
t1.join()
t2.join()
end = time.time()
  
print('Time taken in seconds -', end - start)

输出:

Time taken in seconds - 2.183610439300537

如您所见,在上面的代码中,CPU 绑定进程和多线程进程的两个代码具有相同的性能,因为在 CPU 绑定程序中,因为 GIL 限制 CPU 只能与单个线程一起工作。 CPU 绑定线程和多线程的影响在Python中是一样的。

为什么 GIL 还没有被删除:

GIL 目前还没有改进,因为Python 2 具有 GIL 实现,如果我们在Python 3 中更改它,那么它会给我们带来问题。因此,我们没有删除 GIL,而是改进了 GIL 的概念。暂时不删除 GIL 的原因之一是Python在后端严重依赖 C,而 C 扩展严重依赖 GIL 的实现方法。虽然还有很多方法可以解决 GIL 解决的问题,但大多数都难以实现,并且会拖慢系统速度。

如何处理 Python 的 GIL :

大多数时候,我们使用多处理来防止程序出现 GIL。在此实现中, Python为要运行的每个进程提供不同的解释器,因此在这种情况下,为多进程中的每个进程提供单个线程。

# Python program showing 
# multiprocessing
  
import multiprocessing 
import time
  
COUNT = 50000000
  
def countdown(n):
    while n>0:
        n -= 1
  
if __name__ == "__main__":
    # creating processes 
    start = time.time()
    p1 = multiprocessing.Process(target = countdown, args =(COUNT//2, ))
    p2 = multiprocessing.Process(target = countdown, args =(COUNT//2, ))
  
    # starting process 1 
    p1.start()
    # starting process 2 
    p2.start() 
  
    # wait until process 1 is finished 
    p1.join() 
    # wait until process 2 is finished 
    p2.join()
    end = time.time()
    print('Time taken in seconds -', end - start)

输出:

Time taken in seconds - 2.5148496627807617

如您所见,多线程系统和多处理系统所花费的时间没有区别。这是因为多处理系统有自己的问题需要解决。所以这不会解决问题,但是它提供了 GIL 允许由Python执行的解决方案。