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内部一个策略问题
- GIL会根据执行的字节码行数以及时间片释放GIL
- GIL当遇到IO操作的时候会释放
-
- 比如说add()函数,在add()函数前面执行
- dosomething1
- io操作
- 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) 收藏 举报
浙公网安备 33010602011771号