GIL详解

GIL详解什么是GIL全局解释器锁。它并不是python的特性,是在实现Python解析器(CPython)时所引入的一个概念。像JPython就没有GIL,然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Pyth

大家好,欢迎来到IT知识分享网。GIL详解

什么是GIL

  全局解释器锁。它并不是python的特性,是在实现Python解析器(CPython)时所引入的一个概念。像JPython就没有GIL,然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。

 

GIL是怎么产生的

  python在创建之初只考虑了单核的CPU,而单核情况下,解决多线程之间数据完整性和状态同步的最简单方法自然就是加锁, 于是有了GIL这把超级大锁。而且因为python的线程是调用操作系统的原生线程,这个原生线程就是C语言写的原生线程。因为python是用C写的,启动的时候就是调用的C语言的接口。每个线程在执行的过程中,python解释器是控制不了的,因为是调的C语言的接口,超出了python的控制范围,python的控制范围是只在python解释器这一层,它只能等结果。所以它不能控制让哪个线程先执行,因为是一块调用的,只要一执行,就是等结果。有了GIL,就能够保证同一时刻只有一个线程可以使用CPU。

  各CPU厂商在核心频率上的比赛变成了多核,GIL的缺陷也随之而来。

 

什么时候会释放Gil锁

  1. 遇到像 i/o操作这种 会有时间空闲情况 造成cpu闲置的情况会释放Gil

  2. 会有一个专门ticks进行计数 一旦ticks数值达到100 这个时候释放Gil锁 线程之间开始竞争Gil锁(说明:ticks这个数值可以进行设置来延长或者缩减获得Gil锁的线程使用cpu的时间)

 

当前GIL的问题

  为了让各个线程能够平均利用CPU时间,python会计算当前已执行的微代码数量,达到一定阈值后就强制释放GIL。而这时也会触发一次操作系统的线程调度。这种模式在只有一个CPU核心的情况下毫无问题。任何一个线程被唤起时都能成功获得到GIL(因为只有释放了GIL才会引发线程调度)。但当CPU有多个核心的时候,问题就来了。当线程均为CPU密集型运算线程时,从release GIL到acquire GIL之间几乎是没有间隙的。所以当其他在其他核心上的线程被唤醒时,大部分情况下主线程已经又再一次获取到GIL了。这个时候被唤醒执行的线程只能白白的浪费CPU时间,看着另一个线程拿着GIL欢快的执行着。然后达到切换时间后进入待调度状态,再被唤醒,再等待,以此往复恶性循环。

  简单的总结下就是:Python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降

 

如何解决GIL锁的问题

  1.更换cpython为jpython(不建议)

    因为他是用java实现的解释器,也失去了利用社区众多C语言模块有用特性的机会。所以这些解析器也因此一直都比较小众。

  2.使用多进程完成多线程的任务(用multiprocess替代Thread)

    每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争抢。它的引入会增加程序实现时线程间数据通讯和同步的困难。就拿计数器来举例子,如果我们要多个线程累加同一个变量,对于thread来说,申明一个global变量,用thread.Lock的context包裹住三行就搞定了。而multiprocess由于进程之间无法看到对方的数据,只能通过在主线程申明一个Queue,put再get或者用share memory的方法。这个额外的实现成本使得本来就非常痛苦的多线程程序编码,变得更加痛苦了。

  3.在使用多线程可以使用c语言去实现

 

互斥锁和Gil锁的关系

  Gil锁 : 保证同一时刻只有一个线程能使用到cpu
  互斥锁 : 多线程时,保证修改共享数据时有序的修改,不会产生数据修改混乱

  首先假设只有一个进程,这个进程中有两个线程 Thread1,Thread2, 要修改共享的数据date, 并且有互斥锁
  执行以下步骤:
  (1)多线程运行,假设Thread1获得GIL可以使用cpu,这时Thread1获得 互斥锁lock,Thread1可以改date数据(但并
  没有开始修改数据)

  (2)Thread1线程在修改date数据前发生了 i/o操作 或者 ticks计数满100 (注意就是没有运行到修改data数据),这个
  时候 Thread1 让出了Gil,Gil锁可以被竞争

  (3) Thread1 和 Thread2 开始竞争 Gil (注意:如果Thread1是因为 i/o 阻塞 让出的Gil Thread2必定拿到Gil,如果
  Thread1是因为ticks计数满100让出Gil 这个时候 Thread1 和 Thread2 公平竞争)

  (4)假设 Thread2正好获得了GIL, 运行代码去修改共享数据date,由于Thread1有互斥锁lock,所以Thread2无法更改共享数据
  date,这时Thread2让出Gil锁 , GIL锁再次发生竞争

  (5)假设Thread1又抢到GIL,由于其有互斥锁Lock所以其可以继续修改共享数据data,当Thread1修改完数据释放互斥锁lock,
  Thread2在获得GIL与lock后才可对data进行修改

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/31887.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信