Python GIL
概述
GIL(Global Interpreter Lock)是什么东东?为什么当一些Pythoners在开发一些多线程操作的时候,都会有些很多疑问?多线程真的很糟糕吗?我该如何实现多线程并发操作?今天博主带你详细的介绍一下GIL。
GIL原理
由于Python是动态解释性语言,即解释运行。运行Python代码时都会通过Python解释器解释执行,Python官方默认的解释器是Cython,当然你也可以选择自己的Python解释器(PyPy,JPython),其中JPython就没有GIL的限制。在解释器解释执行任何Python代码时,首先都需要they acquire GIL when running,release GIL when blocking for I/O。如果没有涉及I/O操作,只是CPU密集型操作或者,解释器会每隔100 ticks(低级的解释器指令)就释放GIL(通过 sys.setcheckinterval来修改)。GIL是实现Python解释器(Cython)时所引入的一个概念。GIL不是Python的特性。
线程执行模型
我们先看一下Python下多任务线程执行模型,下面的图取自David Beazley大神,并且在他的个人网站中对GIL进行深度的解剖。如果想了解更深入的东西,可以去逛逛他的网站。
从上图中可以看出,这个是三个线程”协作式“执行,当Thread1执行时它获得GIL,其它线程一直在等待;当遇到I/O处理时,Thread1会释放GIL,Thread2得到GIL,Thread2开始运行,如此反复直到任务完成。当任一个线程正在运行时,它控制着GIL,并且在处理I/O(read,write,send,recv,etc.)时释放GIL。CPU密集型(不提供I/O操作)的线程作为特殊的情况被处理,即每运行100个低级的解释器指令进行检查并根据线程优先级进行释放/重新获取或者释放GIL。
我们来看一段代码:
import threading
import time
def count(n):
while n>0:
n-=1
if __name__ == "__main__":
t1 = time.time()
count(10000000)
count(10000000)
t2 = time.time()
print t2-t1
a = threading.Thread(target=count,args=(10000000,))
a.start()
b = threading.Thread(target=count,args=(10000000,))
b.start()
a.join()
b.join()
t3 = time.time()
print t3-t2
# 输出结果
11.5187261105
18.4223148823
上述的例子是一个很典型的CPU密集任务,threading是Python高级别的线程库,Count只是普通的函数运行在一个主线程内。这就是为什么Python多线程的并不是真正意义上的多线程。Python的Thread是真实操作系统的Thread,两者没有差别。在Linux下是由pthreads实现的,而在windows下是由Windows threads实现的,并通过操作系统调度算法进行调度。为了充分利用CPU,python计算当前已执行了多少数量的指令达到阈值就会立即(100 ticks)来释放GIL。 我们分析一下程序问题: count函数里面主要做的是计算,I/O操作一直没有触发,那么就会一直等待知道100 ticks才会释放GIL。从release GIL到acquire GIL之间几乎是没有间隙的。所以在其他核心上的线程被唤醒时,大部分情况下主线程已经又再一次获取到GIL了。这个时候被唤醒执行的线程只能白白的浪费CPU时间,看着另一个线程拿着GIL欢快的执行着。然后达到切换时间后进入待调度状态,再被唤醒,再等待,以此往复恶性循环。
如何避免GIL影响
- CPU密集型下的任务尽量采用多进程处理(multiprocessing).
- 如果你不想使用Cython解释器,就没有这个限制,同样很多Cython的特性你也放弃了。
- 利用 ctypes 绕过 GIL.ctypes会在调用C函数前释放GIL,可以通过ctypes和C动态库来让 python充分利用物理内核的计算能力。
- 听我说说我的博客: 月访问量过万的个人IT博客的技术史
- TransactionScope和Enterprise Libray 3.0 Data Access Application Block
- 《Python Web开发 - 测试驱动方法》阅后感
- 微信小程序分享——会话服务器和业务服务器合并
- 微信官方开源UI库-WeUI
- ViewFlipper实现多页面切换
- Ubuntu & Fedora Mono 2.8 安装脚本
- android下拉加载更多
- 在 Windows 上安装Rabbit MQ 指南
- CentOS 7 安装RabbitMQ 3.3
- 神经网络
- Node.js Leap Motion Hello World——开启AR的小窗
- 微信小游戏:无法进行网络请求的解决方案
- 微信跳一跳之深度实践
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 【STM32F429开发板用户手册】第27章 STM32F429的定时器应用之TIM1-TIM14的PWM实现
- 数据库基础开源学习教程-android 使用 litepal 操作本地数据库
- 红黑树——动态+静态图
- 一文读懂Python实现张量运算
- javascript之闭包基础了解
- Python中的多处理与多线程:新手简介
- Fortran中的陷阱-NAMELIST
- 当Excel遇到大数据问题,是时候用Python来拯救了
- PySCF程序包平均场计算的一些收敛技巧
- 你应该知道的10个Python文件系统方法
- 适合初学者的Python装饰器的简易教程
- 一起刷Leetcode第一篇,数组和字典的妙用
- 加速Python列表和字典,让你代码更加高效
- 如何使用Python的Flask和谷歌app Engine来构建一个web app
- 如何用Python实现电子邮件的自动化