美文网首页
Python全局解释锁

Python全局解释锁

作者: 卫青臣 | 来源:发表于2022-01-16 20:01 被阅读0次

    翻译:什么是全局解释器锁GIL? - 知乎 (zhihu.com)

    什么是GIL

    全局解释锁(Global Interpreter Lock, GIL)是一个互斥锁,它规定Python解释器同一时刻只允许一个线程运行。

    GIL的优点

    Python 使用引用计数进行内存管理,如果有多个线程参与的竞争条件时(指多个线程同一时间想要修改共享数据),此时我们需要保护引用计数变量以免受竞争条件的影响。如果不对引用计数变量进行保护 ,可能发生内存泄漏(对象所在的内存得不到释放)或者提前释放。这样会造成稀奇古怪的 bug。
    如果我们使用锁保护引用计数变量的话,那么所有共享数据的创建和回收都不会发生上面的问题。
    但在多个锁的情况下,频繁的获取/释放锁会降低程序的性能。而且如果我们给每个对象都加上锁,那么很可能死锁问题(A等待B,B等待C,C等待A)。
    GIL 是解释器层面的一个锁,运行任何一段 Python 字节码的时候,都需要先获得解释器锁,这就使得死锁的情况不可能发生(因为只有一个锁)。

    GIL的缺点

    GIL 对单线程程序丝毫没有影响,但它会造成 CPU 密集型/多线程程序中的瓶颈。
    CPU 密集型程序指的是极致利用 CPU 性能。比如像矩阵乘法,图片处理这样的数学计算。
    IO 密集型程序指的花费大量时间消耗在 输入/输出 操作,比如文件读写,网络数据传输/接收。造成这些是因为 CPU 和速度,内存的速度,硬盘等外设的速度完全不在一个数量级,很多时候程序需要等待它们所需的资源到位才能接着运行。

    举一个CPU密集型程序的例子,先使用单线程执行:

    import time
    from threading import Thread
    
    COUNT = 50000000
    
    def countdown(n):
        while n>0:
            n -= 1
    
    start = time.time()
    countdown(COUNT)
    end = time.time()
    
    print('Time taken in seconds -', end - start)
    

    输出Time taken in seconds - 6.20024037361145
    换成两个线程执行:

    import time
    from threading import Thread
    
    COUNT = 50000000
    
    def countdown(n):
        while n>0:
            n -= 1
    
    t1 = Thread(target=countdown, args=(COUNT//2,))
    t2 = Thread(target=countdown, args=(COUNT//2,))
    
    start = time.time()
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    end = time.time()
    
    print('Time taken in seconds -', end - start)
    

    输出Time taken in seconds - 6.924342632293701
    可以看出单线程和多线程的效率相差无几,在多线程的版本中,由于GIL的存在,实际还是以单线程状态轮流执行两个线程

    解决GIL影响多线程效率的问题

    既然多线程会收GIL影响,那可以使用多进程,每个进程都有自己的解释器和内存空间
    Python中的multiprocessing库可以让程序以多进程执行:

    from multiprocessing import Pool
    import time
    
    COUNT = 50000000
    def countdown(n):
        while n>0:
            n -= 1
    
    if __name__ == '__main__':
        pool = Pool(processes=2)
        start = time.time()
        r1 = pool.apply_async(countdown, [COUNT//2])
        r2 = pool.apply_async(countdown, [COUNT//2])
        pool.close()
        pool.join()
        end = time.time()
        print('Time taken in seconds -', end - start)
    

    输出:Time taken in seconds - 4.060242414474487
    执行效率对比单线程和多线程有了明显提升

    相关文章

      网友评论

          本文标题:Python全局解释锁

          本文链接:https://www.haomeiwen.com/subject/pvofhrtx.html