python之路

PYTHON GIL 理解

2017-08-03  本文已影响13人  彩色系

在理解GIL前 首先要理解下python的线程。

线程执行过程

上图是线程的执行过程。可以看到 GIL确保一个时刻只有一个线程执行。在碰到阻塞 比如IO等待时释放GIL。

一开始

第一步

当线程一遇到一个IO事件后,释放掉GIL

释放GIL

线程二拿到锁,进行上下文切换

切换

有时会出现两个线程都是可用的情况

都可用

这时就通过系统的优先级队列来调度。从就绪队列中拿到优先级高的,然后进行运算。

enter image description here

GIL的影响

通过一个简单的countdown代码来测试

num=1000000
def threaded_at_once():
    threads = [num//2 for i in range(20)]
    results = pool.map(countdown, threads)

def normal():
    for i in range(concurrent):
        countdown(num)

testnum = 10
a = timeit(normal, number=testnum)
b = timeit(threaded_at_once, number=testnum)
print('Normal', a)
print('thread', b)

# py3.5.2
# Normal 6.854097206157869
# thread 6.9655090331886695
# py3.6
# Normal 9.051292152972469
# thread 8.782672920645473
# py2.7
# Normal 3.92715933198
# thread 12.6787057864

可以看到不同版本的python较大这个是由于内部线程切换的逻辑不同带来的。

整体来看gil对程序还是有影响的。使用了多线程并没有带来很显著的提升。

在cpython的解释器中有这样一个函数。

sys.setcheckinterval(interval)
设置解释器的“检查间隔”。此整数值确定解释器检查周期性事件(例如线程切换和信号处理程序)
的频率。默认值为 100,表示每100个Python虚拟指令执行一次检查。将其设置为较大的值可以提高
使用线程的程序的性能。将其设置为值 <= 0检查每个虚拟指令,最大化响应度以及开销。
3.2 版后已移除: 此函数不再有效果,因为线程切换和异步任务的内部逻辑已被重写。
请改用 setswitchinterval()。

对于CPU密集的任务 GIL才会有影响。

为什么cpython要使用gil?

gil的好处:
增加单线程处理的速度
易于集成的C库通常不是线程安全的
在io密集任务线程是更好的选择
使得C扩展更容易编写

为什么有时有了GIL还需要自己手动给一些程序加锁?

GIL锁的层面是python 字节码层面的
而自己实现同步时用的锁是python层面的,相当于是用户锁
执行的粒度不同
一个简单的加一操作有多个原子操作。gil只确保在一个原子操作上是安全的
           dis.dis(lambda x:x+1)
           0 LOAD_FAST                0 (x)
          3 LOAD_CONST               1 (1)
          6 BINARY_ADD
          7 RETURN_VALUE

在遇到性能瓶颈时,将这一部分拿出来,用C扩展,可以获得很大的提升。

https://www.yunxcloud.cn/post/136
参考

http://www.dabeaz.com/python/GIL.pdf

http://www.dabeaz.com/python/UnderstandingGIL.pdf

https://softwareengineering.stackexchange.com/questions/186889/why-was-python-written-with-the-gil

博客 https://www.97up.cn/post/148

https://wiki.python.org/moin/GlobalInterpreterLock

上一篇下一篇

猜你喜欢

热点阅读