python 3.x GIL

python GIL

什么是GIL???

  • global interpreter lock 全局解释器锁
  • 要学习python多线程编程,第一个知识点就是GIL
  • python中的一个线程对应于c语言中的一个线程(基于cpython来解释的,因为目前使用的python默认都是cpython)
  • python前期为了简单,在做多线程编程的时候,python会在解释器上加上一把非常大的锁.
  • GIL使得同一时刻只有一个线程运行在一个cpu上执行字节码,这样的话就保证线程在某种程度上是安全的.
  • 无法将多个线程映射到多个cpu上执行,这样的话无法体现多cpu也就是多核的优势.
  • 只要运行一个python进程,里面不管有多少个线程他都只能一个线程运行在一个cpu上,实际上使用java或者C语言来写的话,可以将多个线程映射到多个cpu上.
  • 所以说python在并发某种程度上就非常的受限,所以python在努力的去GIL化,将GIL这把锁去掉.
  • 因为在前期大量的第三方包都是使用了cpython来完成的,所以说GIL在很长一段时间内是不太可能被去掉的.
  • 但是为了去掉GIL实际上有很多其他解释器,比如pypy是去GIL化的
  • 是不是说一个时刻只有一个线程运行在一个cpu上,他既然是这么一个现实,那是不是意味着我们在编写多线程编码的时候是安全的呢???就不用去考虑线程间的一个同步呢???实际上是不是的.

为了分析这个问题,用一个例子来说明.虽然有GIL这把锁,但GIL是可以释放的

 1 import threading
 2 
 3 total = 0
 4 
 5 
 6 def add():
 7     global total
 8     for i in range(1000000):
 9         total += 1
10 
11 
12 def desc():
13     global total
14     for i in range(1000000):
15         total -= 1
16 
17 
18 # 给函数添加一个线程
19 thread1 = threading.Thread(target=add)
20 thread2 = threading.Thread(target=desc)
21 
22 # 开启线程
23 thread1.start()
24 thread2.start()
25 
26 # 等待所有线程的完成,在执行print语句
27 thread1.join()
28 thread2.join()
29 
30 print(total)
31 
32 # 第一次运行程序的结果:156004
33 # 第二次运行程序的结果:32844
34 # 第三次运行程序的结果:-299762

综上结果分析

  • 每一次运行的结果是不一样的,不是一个稳定的结果,通过上述的例子来说明GIL是可以释放的.
  • GIL的释放在python2和python3中是有区别的,GIL实际上结合了字节码.
  • 首先需要明确一点GIL这把锁分配给某一个线程之后,并不是说这个线程完全执行完之后GIL才会释放,再把它交给另一个线程.
  • GIL不是整个过程完全的占有,GIL实际上会在适当的时刻释放,这个实际上他是结合了字节码执行的一个行数,
    比如说字节码执行了100行之后或者1000行字节码之后GIL会释放,然后另外一个线程可以得到运行,试想一下,如果说thread1线程一直占有GIL,total的值一直加到1000000,total加到1000000之后,释放GIL锁.然后线程thread2就能拿到GIL锁,将total继续减1,减到0为止.
  • 上述的示例代码侧面的证明了GIL不是等待一个线程完成所有操作再去执行另一个线程,而是一个线程执行一段时间后释放GIL,然后GIL切换到另一个线程执行一段时间.
  • 上述示例代码运行过程可以看到,GIL并不是说一个线程一但占有之后就一直占有不释放,所以说GIL是会释放的.
  • GIL会根据执行字节码的行数,比如说thread1执行到1000行之后会释放GIL,将GIL给thread2线程.或者说GIL会通过时间片来划分,比如add()函数执行15ms之后释放GIL,将GIL交给desc()函数来执行,当然这是python内部一个策略问题
  1. GIL会根据执行的字节码行数以及时间片释放GIL
  2. GIL当遇到IO操作的时候会释放
    • 比如说add()函数,在add()函数前面执行  
    1. dosomething1  
    2. io操作  
    3. dosomething3  
      • 如果说1~3这是一个逻辑,这时如果进行IO操作的时候,一直占有GIL,或者IO通过时间片划分占有GIL,实际上效率就低了.    
      • 比如说add()函数和desc()函数都有IO操作,这时add()执行到IO操作的时候,GIL一定要等到IO的时间片完成之后才释放GIL,去执行desc()函数,实际上时间片浪费是很严重的,因为当IO操作时间片很长的话,GIL会一直切来切去的,    
      • 所以说GIL在遇到IO操作的时候会主动释放,将GIL分配给其他线程进行运行,这样的话GIL看起来不会限制的那么死,实际上也是比较灵活的,    
      • 所以说正是因为GIL在遇到IO操作的时候会主动释放这个特性,使得python的多线程在IO操作频繁的情况下,GIL实际上是非常适用的    

 

******GIL在进行python多线程编程是非常重要的*****

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

********

posted on 2019-04-21 08:11  jaydenjune  阅读(55)  评论(0)    收藏  举报

导航