Python中GIL的使用详解


Posted in Python onOctober 03, 2018

1、GIL简介

GIL的全称为Global Interpreter Lock,全局解释器锁。

1.1 GIL设计理念与限制

python的代码执行由python虚拟机(也叫解释器主循环,CPython版本)来控制,python在设计之初就考虑到在解释器的主循环中,同时只有一个线程在运行。即在任意时刻只有一个线程在解释器中运行。对python虚拟机访问的控制由全局解释锁GIL控制,正是这个锁来控制同一时刻只有一个线程能够运行。

在调用外部代码(如C、C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于期间没有python的字节码运行,所以不会做线程切换)。

在python中使用都是操作系统级别的线程,linux中使用的pthread,window使用的是其原生线程。

从上面的概述中可以直观的看出py在同一时刻只能跑一个线程,这样在跑多线程的情况下,只有当线程获取到全局解释器锁后才能运行,而全局解释器锁只有一个,因此即使在多核的情况下也只能发挥出单核的功能。

那么这样看起来py不给力啊,GIL直接导致CPython不能利用物理多核的性能加速运行。那么为什么会有这样的设计?考虑到Guido van Rossum 在创造python的时候,上世纪90年代,多核cpu完全属于不可想象的,现在由于硬件发展速度太快,程序编写就要考虑用尽cpu的全部性能,否则就要被淘汰,那么对于python同样也要如此。

上面主要说的是这种设计的劣势,下面再讨论它的优势。

GIL的设计简化了CPython的实现,使得对象模型,包括关键的内建类型如字典,都隐式可以并发访问。锁住全局解释器使得其比较容易的实现对多线程的支持,但也折损了多处理器主机的并行计算能力。

但是不论标准的,还是第三方的扩展模块,都被设计成在进行密集计算任务时释放GIL。另外还有在做IO操作时,GIL总是被释放。对所有面对内建的操作系统C代码的程序来说,GIL会在这个IO调用之前被释放,以允许其它的线程在等待这个IO的时候运行。如果是纯计算的程序,没有IO操作,解释器会每隔100次或每隔一定时间15ms去释放GIL。

这里可以理解为IO密集型的python比计算密集型的程序更能利用多线程环境带来的便利。

1.2 GIL对线程执行的影响

多线程环境中,python虚拟机按照以下方式执行:

  1. 设置GIL
  2. 切换到一个线程去执行
  3. 运行代码,这里有两种机制:
    1. 指定数量的字节码指令(100个)
    2. 固定时间15ms线程主动让出控制
  4. 把线程设置为睡眠状态
  5. 解锁GIL
  6. 再次重复以上步骤

上节说到python语言和程序一样要考虑用尽cpu的性能,下面在讨论py的应对方法。

python的应对方法很简单,在新的python3中依然有GIL,原因大概有下几点:

  • CPython的GIL本意是用来保护所有全局的解释器和环境状态变量的,如果去掉GIL,就需要更多的更细粒度的锁对解释器的众多全局状态进行保护。或者采用Lock-Free算法。无论采用哪一种,要做到多线程安全都会比维系一个GIL要难得多。另外改动的还是CPython的代码树及其各种第三方扩展也在依赖GIL。
  • 进一步说,有人做过测试将GIL去掉,加入更细粒度的锁。但是实践检测对单线程来说,性能更低。只有利用的物理cpu到一定数目后,性能才会比GIL版本好。且现在绝大部分的python程序都是单线程的。

然后最重要的还在于以下几个方面,简单来说就是py不改,一样能实现我们的需求。

  • 自2.6引出的多进程标准库mutilprocessing,让多进程的python编写简化到类似多线程的程度,大大减轻GIL带来的诸多不利。
  • 利用ctypes绕过GIL:ctypes可以使py直接调用任意的C动态库的导出函数。所要做的只是用ctypes写python代码即可。而且,ctypes会在调用C函数前释放GIL。

python中GIL使得同一个时刻只有一个线程在一个cpu上执行,无法将多个线程映射到多个cpu上执行,但GIL并不会一直占有,它会在适当的时候释放

import threading
count = 0
def add():
  global count
  for i in range(10**6):
    count += 1

def minus():
  global count
  for i in range(10**6):
    count -= 1

thread1 = threading.Thread(target=add)
thread2 = threading.Thread(target=minus)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(count)

分别运行三次的结果:

-59452
60868
-77007

可以看到count并不是一个固定值,说明GIL会在某个时刻释放,那么GIL具体在什么情况下释放呢:

1.执行的字节码行数到达一定阈值

2.通过时间片划分,到达一定时间阈值

3.在遇到IO操作时,主动释放

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

Python 相关文章推荐
Python 2.7.x 和 3.x 版本的重要区别小结
Nov 28 Python
python判断图片宽度和高度后删除图片的方法
May 22 Python
python处理html转义字符的方法详解
Jul 01 Python
利用标准库fractions模块让Python支持分数类型的方法详解
Aug 11 Python
20行python代码的入门级小游戏的详解
May 05 Python
python判断文件夹内是否存在指定后缀文件的实例
Jun 10 Python
python创建学生成绩管理系统
Nov 22 Python
python numpy数组中的复制知识解析
Feb 03 Python
Java byte数组操纵方式代码实例解析
Jul 22 Python
python文件名批量重命名脚本实例代码
Apr 22 Python
python中Pyqt5使用Qlabel标签播放视频
Apr 22 Python
Python 第三方库 openpyxl 的安装过程
Dec 24 Python
Python线程同步的实现代码
Oct 03 #Python
详解通过API管理或定制开发ECS实例
Sep 30 #Python
Python 使用类写装饰器的小技巧
Sep 30 #Python
浅谈django三种缓存模式的使用及注意点
Sep 30 #Python
使用Python实现租车计费系统的两种方法
Sep 29 #Python
Python实现App自动签到领取积分功能
Sep 29 #Python
10个Python小技巧你值得拥有
Sep 29 #Python
You might like
用php实现的获取网页中的图片并保存到本地的代码
2010/01/05 PHP
PHP SEO优化之URL优化方法
2011/04/21 PHP
php.ini-dist 和 php.ini-recommended 的区别介绍(方便开发与安全的朋友)
2012/07/01 PHP
PHP删除数组中空值的方法介绍
2014/04/14 PHP
2014年10个最佳的PHP图像操作库
2014/07/14 PHP
在win系统安装配置 Memcached for PHP 5.3 图文教程
2015/03/03 PHP
Thinkphp框架+Layui实现图片/文件上传功能分析
2020/02/07 PHP
5种处理js跨域问题方法汇总
2014/12/04 Javascript
vue.js指令v-model实现方法
2016/12/05 Javascript
JavaScript中从setTimeout与setInterval到AJAX异步
2017/02/13 Javascript
微信小程序 开发MAP(地图)实例详解
2017/06/27 Javascript
利用ES6实现单例模式及其应用详解
2017/12/09 Javascript
Vue cli 引入第三方JS和CSS的常用方法分享
2018/01/20 Javascript
jQuery 改变P标签文本值方法
2018/02/24 jQuery
Vue中mintui的field实现blur和focus事件的方法
2018/08/25 Javascript
vue单页应用在页面刷新时保留状态数据的方法
2018/09/21 Javascript
js 对象使用的小技巧实例分析
2019/11/08 Javascript
jQuery单页面文字搜索插件jquery.fullsearch.js的使用方法
2020/02/04 jQuery
[01:48]DOTA2 2015国际邀请赛中国区预选赛第二日战报
2015/05/27 DOTA
[47:52]完美世界DOTA2联赛PWL S2 PXG vs InkIce 第二场 11.26
2020/11/30 DOTA
Python实现队列的方法
2015/05/26 Python
深入解析Python中的list列表及其切片和迭代操作
2016/03/13 Python
CentOS 6.5中安装Python 3.6.2的方法步骤
2017/12/03 Python
python 重命名轴索引的方法
2018/11/10 Python
python存储16bit和32bit图像的实例
2018/12/05 Python
python中numpy数组与list相互转换实例方法
2021/01/29 Python
HTML5画渐变背景图片并自动下载实现步骤
2013/11/18 HTML / CSS
银行出纳岗位职责
2013/11/25 职场文书
晚宴邀请函范文
2014/01/15 职场文书
索桥的故事教学反思
2014/02/06 职场文书
工程专业求职自荐书范文
2014/02/08 职场文书
2014年环保局工作总结
2014/12/11 职场文书
投标承诺函范文
2015/01/21 职场文书
2015年公共机构节能宣传周活动总结
2015/03/26 职场文书
致创业您:正能量激励人心句子(48条)
2019/08/15 职场文书
win10滚动条自动往上跑怎么办?win10滚动条自动往上跑的解决方法
2022/08/05 数码科技