Python之GIL

>GIL为什么物

GIL(Global Interpreter Lock),也称为全局解释器,看下官方解释python

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)数据库

主要意思为:安全

GIL是一个互斥锁,它防止多个线程同时执行Python字节码。这个锁是必要的,主要是由于CPython的内存管理不是线程安全的bash

>Python与GIL

Python的GIL只是CPython(Python解释器)的一个问题,那么Python又有哪些解释器,它们也存在和CPython一样的问题吗?那么什么又是解释器呢?网络

什么是解释器多线程

咱们写的代码计算机是如何识别的呢,计算机也拥有和人类相同的思惟和语言吗?显然不是的;计算机只能识别机器指令语言也就是0和1,那么咱们编写的程序计算机是如何识别的呢?这就是解释器的做用了,解释器将咱们编写的Python代码翻译为机器指令语言,Python解释器自己也是个程序,它是解释Python代码的,叫作解释器.并发

Python解释器有哪些性能

  1. CPython: 官方默认版本,使用C语言开发,是Python使用最普遍的解释器,有GIL.
  2. IPython: IPython是基于CPython之上的交互式解释器,其它方面和CPython相同.
  3. PyPy: PyPy采用JIT(Just In Time)也就是即时编译编译器,对Python代码执行动态编译,目的是加快执行速度,有GIL.
  4. Jython: 运行在Java平台上的解释器,把Python代码编译为Java字节码执行,没有GIL.
  5. IronPython: IronPython和Jython相似,只不过IronPython是运行在微软.Net平台上的Python解释器,能够直接把Python代码编译成.Net的字节码,没有GIL.

>GIL解决了Python什么问题呢

Python内部对变量或数据对象使用了引用计数器,咱们经过计算引用个数,当个数为0时,变量或者数据对象就被自动释放测试

In [1]: import  sys                                                  

In [2]: count_var = "test1"                                         

In [3]: sys.getrefcount(count_var)                                  
Out[3]: 2

In [4]: add_var = count_var                                         

In [5]: sys.getrefcount(add_var)                                    
Out[5]: 3
复制代码

这个引用计数器须要保护,当多个线程同时修改这个值时,可能会致使内存泄漏;SO,咱们使用锁来解决这个问题,可有时会添加多个锁来解决,这就会致使另个问题,死锁;ui

为了不内存泄漏和死锁问题,CPython使用了单锁,即全局解释器锁(GIL),即执行Python字节码都须要获取GIL,这能够防止死锁,但它有效地使任何受CPU限制的Python程序都是单线程.

>GIL对多线程Python程序的影响

程序的性能受到计算密集型(CPU)的程序限制和I/O密集型的程序限制影响,那什么是计算密集型和I/O密集型程序呢?

计算密集型(CPU)

高度使用CPU的程序,例如: 进行数学计算,矩阵运算,搜索,图像处理等.

I/O密集型

I/0(Input/Output)程序是进行数据传输,例如: 文件操做,数据库,网络数据等


测试下顺序执行单线程和并发执行多线程的效率

- 顺序执行单线程(single_thread.py)

import threading
import time

def test_counter():
    i = 0
    for _ in range(100000000):
        i += 1
    return True

def main():
    start_time = time.time()
    for tid in range(2):
        t1 = threading.Thread(target=test_counter)
        t1.start()
        t1.join()
    end_time = time.time()
    print("Total time:{}".format(end_time-start_time))


if __name__ == "__main__":
    main()
复制代码

执行结果:

Total time: 11.299654722213745
复制代码

- 并发执行两个线程(multi_thread.py)

import threading
import time

def test_counter():
    i = 0
    for _ in range(100000000):
        i += 1
    return True

def main():
    thread_array = {}
    start_time = time.time()
    for tid in range(2):
        t = threading.Thread(target=test_counter)
        t.start()
        thread_array[tid] = t
    for i in range(2):
        thread_array[i].join()
    end_time = time.time()
    print("Total time:{}".format(end_time-start_time))


if __name__ == "__main__":
    main()
复制代码

执行结果:

Total time:13.7098388671875
复制代码

GIL对I/O绑定多线程程序的性能影响不大,由于线程在等待I/O时共享锁.

GIL对计算型绑定多线程程序有影响,例如: 使用线程处理部分图像的程序,不只会因锁定而成为单线程,并且还会看到执行时间的增长,这种增长是由锁的获取和释放开销的结果.

>能够去掉累赘GIL吗

有大佬试过,只能说结果不尽人意0 .0,等待着吧

>SO,如何处理Python中的GIL

  • 计算密集型程序
    • 使用多进程(什么是多进程呢,后续道来)
    • 使用其它语言(将计算密集程序放到其它语言中执行)
    • 替换解释器(能够本身尝试)
    • 等大神解决GIL0 .0
  • I/O密集型程序
    • 使用多线程
    • 使用多进程
    • 使用多进程+多线程

Referce

What is the Python Global Interpreter Lock (GIL)?

相关文章
相关标签/搜索