python gil_Python之GIL

>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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值