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使用xmlrpc实例讲解
Dec 17 Python
python将txt文件读取为字典的示例
Dec 22 Python
用Python读取几十万行文本数据
Dec 24 Python
Python字符串通过'+'和join函数拼接新字符串的性能测试比较
Mar 05 Python
pyqt实现.ui文件批量转换为对应.py文件脚本
Jun 19 Python
PyQt5笔记之弹出窗口大全
Jun 20 Python
python基础教程之while循环
Aug 14 Python
Python使用selenium + headless chrome获取网页内容的方法示例
Oct 16 Python
python对象销毁实例(垃圾回收)
Jan 16 Python
TensorFlow中如何确定张量的形状实例
Jun 23 Python
pytorch中的weight-initilzation用法
Jun 24 Python
Python fileinput模块如何逐行读取多个文件
Oct 05 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
rephactor 优秀的PHP的重构工具
2011/06/09 PHP
10个php函数实用却不常见
2015/10/13 PHP
Zend Framework开发入门经典教程
2016/03/23 PHP
php array_map使用自定义的函数处理数组中的每个值
2016/10/26 PHP
js获取浏览器的可视区域尺寸的实现代码
2011/11/30 Javascript
jQuery学习笔记之总体架构
2014/06/03 Javascript
理解javascript中DOM事件
2015/12/25 Javascript
jQuery插件实现图片轮播特效
2016/06/16 Javascript
BootStrap modal模态弹窗使用小结
2016/10/26 Javascript
Angular2入门--架构总览
2017/03/29 Javascript
基于JS对象创建常用方式及原理分析
2017/06/28 Javascript
JavaScript设计模式之构造器模式(生成器模式)定义与用法实例分析
2018/07/26 Javascript
JS中封装axios来管控api的2种方式
2019/09/11 Javascript
vue + elementUI实现省市县三级联动的方法示例
2019/10/29 Javascript
[51:15]完美世界DOTA2联赛PWL S2 PXG vs Magma 第一场 11.21
2020/11/24 DOTA
跟老齐学Python之关于类的初步认识
2014/10/11 Python
Python and、or以及and-or语法总结
2015/04/14 Python
浅谈插入排序算法在Python程序中的实现及简单改进
2016/05/04 Python
Python2随机数列生成器简单实例
2017/09/04 Python
Python网络编程 Python套接字编程
2017/09/13 Python
Python输入二维数组方法
2018/04/13 Python
pandas的唯一值、值计数以及成员资格的示例
2018/07/25 Python
使用Python如何测试InnoDB与MyISAM的读写性能
2018/09/18 Python
Python基本数据结构与用法详解【列表、元组、集合、字典】
2019/03/23 Python
关于python多重赋值的小问题
2019/04/17 Python
创建Django项目图文实例详解
2019/06/06 Python
简单分析python的类变量、实例变量
2019/08/23 Python
Python读写操作csv和excle文件代码实例
2020/03/16 Python
Python3.7 读取音频根据文件名生成脚本的代码
2020/04/07 Python
Python脚本破解压缩文件口令实例教程(zipfile)
2020/06/14 Python
美国Lolё官网:购买大胆而美丽的女性运动服装
2017/05/22 全球购物
联想智利官方网站:Lenovo Chile
2020/06/03 全球购物
美术师范毕业生自荐信
2013/11/16 职场文书
简历中个人自我评价范文
2013/12/26 职场文书
科技活动周标语
2014/10/08 职场文书
python 实现图与图之间的间距调整subplots_adjust
2021/05/21 Python