• GIL全局解释锁


    一 介绍

    官方定义:

    在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势


    GIL 的功能是:在 CPython 解释器中执行的每一个 Python 线程,都会先锁住自己,以阻止别的线程执行。


    首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。有名的编译器例如GCC,INTEL C++,Visual C++等。

    Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。

    像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。

    所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL


    二 GIL介绍

    GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。


    假设有两个 Python 线程同时引用变量 a,那么双方就都会尝试操作该数据,很有可能造成引用计数的条件竞争,导致引用计数只增加 1(实际应增加 2)

    这造成的后果是,当第一个线程结束时,会把引用计数减少 1,此时可能已经达到释放内存的条件(引用计数为 0,垃圾回收机制),当第 2 个线程再次视图访问 a 时,就无法找到有效的内存了。

    所以,CPython 引进 GIL全局锁,可以最大程度上规避类似内存管理这样复杂的竞争风险问题。


    首先确定一点:每次执行python程序,都会产生一个独立的进程。例如python test.py,python aaa.py,python bbb.py会产生3个不同的python进程

    '''
    #验证python test.py只会产生一个进程
    #test.py内容
    import os,time
    print(os.getpid())
    time.sleep(1000)
    '''
    python3 test.py 
    #在windows下查看进程
    tasklist |findstr python
    

    在一个python的进程内,不仅有test.py的主线程或者由该主线程开启的其他线程,还有解释器开启的垃圾回收等解释器级别的线程,总之,所有线程都运行在这一个进程内,毫无疑问

    1 所有数据都是共享的,这其中,代码作为一种数据也是被所有线程共享的(test.py的所有代码以及Cpython解释器的所有代码)

    2 所有线程的任务,都需要将任务的代码当做参数传给解释器(的代码)去执行,即所有的线程要想运行自己的任务,首先需要解决的是能够访问到解释器(的代码)。

    上面这张图,就是 GIL 在 Python 程序的工作示例。

    其中,Thread 1、2、3 轮流执行,每一个线程在开始执行时,都会锁住 GIL,以阻止别的线程执行;同样的,每一个线程执行完一段后,会释放 GIL,以允许别的线程开始利用资源。

    了解:

    其实,CPython 中还有另一个机制,叫做间隔式检查(check_interval)

    意思是 CPython 解释器会去轮询检查线程 GIL 的锁住情况,每隔一段时间,Python 解释器就会强制当前线程去释放 GIL,这样别的线程才能有执行的机会。


    # 三 GIL与多线程

    多个线程先访问到解释器,即拿到执行权限(多个线程都去抢锁,抢到的先执行),然后将任务交给解释器去执行

    解释器的代码是所有线程共享的

    所以垃圾回收线程也可能访问到解释器的代码而去执行,这就导致了一个问题:对于同一个数据100,可能线程1执行x=100的同时,而垃圾回收执行的是回收100的操作

    解决这种问题没有什么高明的方法,就是加锁处理,如下图的GIL,保证python解释器同一时间只能执行一个任务的代码


    有了GIL的存在,同一时刻同一进程中只有一个线程被执行

    听到这里,有的同学立马质问:进程可以利用多核,但是开销大,而python的多线程开销小,但却无法利用多核优势,也就是说python没用

    别着急

    要解决这个问题,我们需要在几个点上达成一致:

    1. cpu到底是用来做计算的,还是用来做I/O的?
    2. 多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能
    3. 每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处

    结论:

    对计算来说,cpu越多越好,但是对于I/O来说,再多的cpu也没用


    了解:

    有了 GIL,并不意味着 Python 程序员就不用去考虑线程安全了,因为即便 GIL 仅允许一个 Python 线程执行,但别忘了 Python 还有 check interval 这样的抢占机制。

  • 相关阅读:
    jekyll简单使用
    三、ansible简要使用
    四、ansible主机组定义
    项目中远程连接404 NOT FOUND问题的原因以及解决办法(这里只涉及我遇到的问题)
    AS3中的位操作
    AS3中is和as操作符的区别
    static 函数和普通函数的区别
    [译] SystemTap
    2017-09-17 python 学习笔记
    xargs 命令使用小记
  • 原文地址:https://www.cnblogs.com/kai-/p/12011187.html
Copyright © 2020-2023  润新知