>GIL为何物
GIL(Global Interpreter Lock),也称为全局解释器,看下官方解释
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的内存管理不是线程安全的
>Python与GIL
Python的GIL只是CPython(Python解释器)的一个问题,那么Python又有哪些解释器,它们也存在和CPython同样的问题吗?那么什么又是解释器呢?
什么是解释器
我们写的代码计算机是如何识别的呢,计算机也拥有和人类相同的思维和语言吗?显然不是的;计算机只能识别机器指令语言也就是0和1,那么我们编写的程序计算机是如何识别的呢?这就是解释器的作用了,解释器将我们编写的Python代码翻译为机器指令语言,Python解释器本身也是个程序,它是解释Python代码的,叫做解释器.
Python解释器有哪些
- CPython: 官方默认版本,使用C语言开发,是Python使用最广泛的解释器,有GIL.
- IPython: IPython是基于CPython之上的交互式解释器,其它方面和CPython相同.
- PyPy: PyPy采用JIT(Just In Time)也就是即时编译编译器,对Python代码执行动态编译,目的是加快执行速度,有GIL.
- Jython: 运行在Java平台上的解释器,把Python代码编译为Java字节码执行,没有GIL.
- 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,我们使用锁来解决这个问题,可有时会添加多个锁来解决,这就会导致另个问题,死锁;
为了避免内存泄漏和死锁问题,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)?