浅谈GIL

GIL全局解释锁

GIL演示

在python中,实际上多线程都是假的,因为有个GIL(全局解释锁),无论你有多少个线程,在最终执行的时候都只有一个CPU被调用

如果要真正意义上实现多线程的话可以使用C语言作为扩展

#单核CPU

在解释GIL问题,我先演示一下单核CPU和多核CPU

这里是我单核CPU的处理情况

[外链图片转存失败(img-A7hOYxTk-1568104730142)(C:\Users\LY\Pictures\博客照片\05.png)]

然后我运行一个死循环

while True:
	pass

[外链图片转存失败(img-Rz4E3yYY-1568104700261)(C:\Users\LY\Pictures\博客照片\06.png)]

CPU一下子就满了

#多核CPU

接下来试试两核的CPU

这里我创建一个子线程也是死循环,主线程也是死循环,那么就有两个死循环了

代码如下:

import threading

def test():
    while True:
        pass

t1=threading.Thread(target=test)
t1.start()

while True:
    pass

执行前我的资源如下:

[外链图片转存失败(img-b9aDhOlE-1568104700262)(C:\Users\LY\Pictures\博客照片\1568100759988.png)]

执行后:

[外链图片转存失败(img-T6vayXGa-1568104700263)(C:\Users\LY\Pictures\博客照片\1568100789313.png)]

哦吼,两个CPU却不是100%,而是两个各占50%

我们从理论上来说,我们两个线程应该各占一个CPU

如图:

[外链图片转存失败(img-Pb5RIEwd-1568104700263)(C:\Users\LY\Pictures\博客照片\1568101064688.png)]

但实际上调用的时候,Python有一个GIL,也就是全局解释锁

[外链图片转存失败(img-9dQVyVea-1568104700264)(C:\Users\LY\Pictures\博客照片\1568101174168.png)]

GIL会控制两个线程,线程1来就给一个CPU去执行,线程1执行完再给线程2给一个CPU执行,实际上是单线程的方式执行的

那么一定要解决这个问题怎么办呢,这个可以用C语言去扩展来解决这个问题

#C语言扩展实现多线程

C语言的代码如下

void DeadLoop()
{
        while(1)
        {
                ;
        }
}

然后在当前目录输入如下命令

gcc loop.c -shared -o libdeadloop.so

然后编写Python代码

from threading import Thread
from ctypes import *


lib = cdll.LoadLibrary("./libdeadloop.so")

t = Thread(target=lib.DeadLoop)

t.start()

#lib.DeadLoop()

while True:
    pass

然后运行python代码

[外链图片转存失败(img-J80xvUTa-1568104700265)(C:\Users\LY\Pictures\博客照片\1568102544981.png)]

一下子就满了

GIL是导致python变慢的原因吗

GIL锁是当初设计python的时候为了数据安全所做的决定,程序执行的时候都要经过GIL,而GIL分配一个程序给一个CPU,所以每个CPU都只能执行一个线程,所以GIL的原因导致python无法使用多CPU的特性,但是GIL是会释放的

在python2.x的中,GIL会按字节码的执行的数量来释放。

在python3.x中会按照时间片来释放,即隔一段时间就释放一次,遇到IO也会释放、或者遇到其他的一些需要时间很多的部分也会释放

在IO型操作而言

IO操作是整个系统中最占时间的部分,比一个单单的线程要高出上千上万倍,如果遇到IO还阻塞的话那真的是会让程序变得很慢了。即便是有很多线程,每个线程切换的时间都要远远低于IO操作,这部分切换的时间在运行程序中是微不足道的

所以,这样在IO型操作就相比于java等语言也不会慢到哪里去

在计算型操作而言

计算型任务基本上是完全占CPU,这个时候要么就用C语言重写关键部分,要么就直接调用多进程,一个进程就一个GIL,这样也可以完美的解决这个问题

计算型操作这里我拿个案例来说明

from threading import Thread

total=0
def add():
    global total
    for i in range(1000000):
        total+=1

def desc():
    global total
    for i in range(1000000):
        total-=1

if __name__ == "__main__":
    addtest=Thread(target=add)
    desctest=Thread(target=desc)

    addtest.start()
    desctest.start()

    addtest.join()
    desctest.join()

    print(total)
    
    
    #611367
    #-433619

执行结果都不一样

如果python没有时间片释放机制,那么肯定是执行完add再去执行desc,结果肯定是0。就是因为有时间片释放机制,所以每次的时间都不一样

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值