GIL的全称为Lock, 其意思是全局解释器锁, 这个GIL并非其特性, 它是仅在解释器里得以引入的某个概念, 而于其他语言所编写成的解释器里面就不存在这个GIL, 例如Pypy。
为什么会有gil?:
因为电脑出现多核CPU以及CPU频率得到提升因而为充分利用多核处理器多线程编程方式更加普及, 随之而来的困难是线程间数据一致性和状态同步的问题, 又因利用了多核所以避免不了这个困难存在, 为解决数据无法同步的问题故而设计了gil全局解释器锁。
提及gil解释器锁, 我们常常会想到, 在多线程里来共享全局变量之际, 会存在线程会针对全局变量作出资源竞争, 会致使对全局变量的修改生成并非我们期望的状貌, 而在那个时候我们所运用的是处于线程模块当中的互斥锁, 如此一来每次针对全局变量开展操作之时, 仅有一个线程能够获取到这个全局变量;瞧瞧下面的代码:
importthreading global_num =0deftest1():globalglobal_numforiinrange(1000000): global_num +=1print("test1",global_num)deftest2():globalglobal_numforiinrange(1000000): global_num +=1print("test2",global_num) t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) t1.start() t2.start()
在上述的示例里, 我们构建了两个线程, 用以对相关操作进行加一竞赛, 然而最终呈现的结果, 并非是我们所期望达成的那般, 因此我们于此处增添了互斥锁。
importthreadingimporttime global_num =0lock = threading.Lock()deftest1():globalglobal_num lock.acquire()foriinrange(1000000): global_num +=1lock.release()print("test1",global_num)deftest2():globalglobal_num lock.acquire()foriinrange(1000000): global_num +=1lock.release()print("test2",global_num) t1 = threading.Thread(target=test1) t2 = threading.Thread(target=test2) start_time = time.time() t1.start() t2.start()
这一回能够获取到我们所期望的结果, 在先前我的博客文章当中曾经提及过这个问题, 在此处就不再过多啰嗦了。
在解释器当中, 当咱们的代码存在一个线程着手访问解释器之际, GIL会将那个大锁锁住, 这时其他的线程就只能干巴巴地等着, 没办法对解释器的资源展开访问, 这一情况同咱们的互斥锁相仿。而仅限这个历程发生在咱们这儿, 并且也要等这个线程被分配的时间到达后, 该线程把gil释放掉, 跟咱们互斥锁的lock.()情形类似, 另外的线程才开始运转, 严格来讲, 这无疑同样是个单线程。
瞅瞅下面这幅图, 仅仅是把我第一段代码里头的变量换成count, 针对此处我们不理睬互斥锁, 单单去思考gil。
执行过程:1.拿到全局变量count
2.申请到解释器的gil
3.解释器调用系统原生线程
4.在cpu1上执行规定的时间
5.执行的时间已然到了, 有着要求去释放gil , 等到下一次获取到gil的时候, 该程序会从这里接续着本次开启执行。
6.拿到了全局变量, 这时针对全局count的操作尚未完成, 所以所拿到的以及拿到的count实际上是一样的, 像这样也就易于解释为何结果不是200万 而是少于200万。
7.申请到了gil锁
8.调用原生的线程
9.若为单核的中央处理器, 那么便会放置于中央处理器1上去运行, 接着, 并非关键之处在于, 就如同在中央处理器3上进行运行这般。
10.执行规定的时间,此时完成了对count的加一操作
11.此时此刻, 执行时间尚未抵达, 线程2完成了针对count的操作, 随之将count进行了加一的动作, 并且随之释放了gil锁。
12.线程1又申请到了gil锁,重复之前的操作。
13.线程1, 对count执行操作, 数量为万, 完成操作时, 执行时间已到, 此时释放gil锁。
综合上面的步骤就能很好的理解gil锁
哪些情况适合用多线程呢:
只要当前正处于开展耗时的IO操作的时段, 能够将GIL释放掉, 因此只要是处于IO密集型的代码范围之中, 运用多线程便是颇为适宜的。
哪些情况适合用多进程呢:
用于计算密集型,比如计算某一个文件夹的大小。