Readventurer

重新出发去冒险, 在郎飞结与地壳的起伏山峰

飞翔的鸟插图。

Python 之gil之二


之前说到在gil的控制下,如果一个cpu富集(这个词可能不是标准译法,英文cpu bound,指运行时间主要由cpu运算的时间来决定的任务)线程获取了gil锁,然后它会一直运行下去,为了使得其他的线程也能够运行,每次过100tick,线程会阻塞自身,然后由python解释器来进行一次check。查看是否有其他的线程在等待获取gil

 
 

首先来谈这个tick,这个tick其实是一定的步数的字节码,如果使用python提供的反汇编模块dis.dis来对python代码进行反汇编将之转变成机器码的话,可以看到


如图所示,这些字节码分为四个tick

(这里有一点自己现在不明白,这个tick到底是按照什么规则来分配的,为什么有的tick比较长,而有的tick比较短,甚至

有的tick的长度会很夸张,比如在这个程序中

  • -1 in xrange(100000000)
    • Ctrl+c ctrl+c ctrl+c…….

    在这个程序中程序会顺序地生成一千万,然后使用1来与其中的每个数来比较,

    比较有意思的是,tick有一个性质,一个tick是不可被中断的,在上面的例子中,这条语句被编译成一个tick,而这个tick运行很耗时。

     
     

    于是我们不得不等等这个tick运行完毕,可以需足足等待数秒的时间。

    (感觉这里自己的理解可能是有个bug,如果按照这个意思来理解的话,那么岂不是说一个tick也有可能长达数小时,但是这样的长tick极少)

    但是有一点是肯定,tick的运行是不基于时间的,一个tick的长短与钟表时间的长短无关。

     
     

    于是在等待100tick之后,线程释放cpu,由python解释器来进行检查。

    这个检查主要检查二个内容。

     
     

    一来检查是否有挂起的信号需要处理,二来当前线程释放掉gil,然后重新来对gil进行抢夺。

     
     

    要理解这一点,又要提到posix协议中的信号.POSIX是一个操作系统的标准,学习过操作系统的童鞋应该都懂。

    至于信号,是unix 以及linux这些类unix系统遵守POSIX协议的一个线程同步的实现方式。

    信号是一种异步的进程间同步机制,表示系统中发生了某一事件,比如经常使用的ctrl+c就是sigint信号。

    这里需要注意的点是,一般当发生某一事件之后,发生线程向操作系统通信,然后发出一个信号,操作系统简单地处理这个信号,并将这个信号发送给其他的线程,或者某一个指定的线程。

     
     

    对于接收信号的程序来讲,程序中会实现一个信号处理函数,由这个函数来对这个信号进行响应,而如果程序没有对信号处理函数进行编程实现,那么就由操作系统进行默认的处理。

    这里面有二点极为重要。

    一是在python中,只有主线程能够处理信号
    ,其他的线程中没有信号处理函数,无法处理信号

    二是大部分信号在操作系统分配后,由程序的信号处理函数来处理,但是对于是kill程序发送的sigkill信号,操作系统是不允许程序来处理的。

     
     

    理解了信号,就可以来看一看,在之前的check过程中,到底发生了什么。