线程安全及Python中的GIL原理分析


Posted in Python onOctober 29, 2019

本文讲述了线程安全及Python中的GIL。分享给大家供大家参考,具体如下:

摘要

什么是线程安全? 为什么python会使用GIL的机制?

在多核时代的到来的背景下,基于多线程来充分利用硬件的编程方法也不断发展起来, 但是一旦 牵扯到多线程,就必然会涉及到一个概念,即 线程安全, 本文就主要谈下笔者对线程安全的一些理解.

而Python为很多人所抱怨的一点就是GIL,那么python为什么选择使用GIL, 本文也就这个问题进行一些讨论.

引入

你的PC或者笔记本还是单核吗? 如果是,那你已经out了.

随着纳米技术的不断进步, 计算机芯片的工艺也在进步,但是已经很难在工艺上的改进来提高 运算速度而满足 摩尔定理, 所以intel, amd相继在采用横向的扩展即增加更多的CPU, 从而双核, 4核, N核不断推出,于是我们进入了多核时代.

于是一个问题出现了, 多核时代的出现对于我们程序员而言意味着什么, 我们如何利用多核的优势?

在回答这个问题之前,建议对 进程 和 线程 不熟悉的读者可以先补下相关的知识.

当然方案是,可以采用 多进程, 也可以采用 多线程. 二者的最大区别就是, 是否共享资源, 后者是共享资源的,而前者是独立的. 所以你也可能想起了google chrome为什么又开始使用独立的进程 来作为每个tab服务了(不共享数据,意味着有更好的安全性).

相对于进程的轻型特征,多线程环境有个最大的问题就是 如何保证资源竞争,死锁, 数据修改等.

于是,便有了 线程安全 (thread safety)的提出.

线程安全

Thread safety is a computer programming concept applicable in the context of multi-threaded programs.
A piece of code is thread-safe if it functions correctly during simultaneous execution by multiple threads.
In particular, it must satisfy the need for multiple threads to access the same shared data,
and the need for a shared piece of data to be accessed by only one thread at any given time.

上面是wikipedia中的解释, 换句话说, 线程安全 是在多线程的环境下, 线程安全能够保证多个线程同时执行时程序依旧运行正确, 而且要保证对于共享的数据,可以由多个线程存取,但是同一时刻只能有一个线程进行存取.

既然,多线程环境下必须存在资源的竞争,那么如何才能保证同一时刻只有一个线程对共享资源进行存取?

加锁, 对, 加锁可以保证存取操作的唯一性, 从而保证同一时刻只有一个线程对共享数据存取.

通常加锁也有2种不同的粒度的锁:

  • fine-grained(所谓的细粒度), 那么程序员需要自行地加,解锁来保证线程安全
  • coarse-grained(所谓的粗粒度), 那么语言层面本身维护着一个全局的锁机制,用来保证线程安全

前一种方式比较典型的是 java, Jython 等, 后一种方式比较典型的是 CPython (即Python).

前一种本文不进行讨论, 具体可参考 java 中的多线程编程部分.

至于Python中的全局锁机制,也即 GIL (Global Interpreter Lock), 下面主要进行一些讨论.

GIL

什么是 GIL ? 答案可参考wikipedia中的说明, 简单地说就是:

每一个interpreter进程,只能同时仅有一个线程来执行, 获得相关的锁, 存取相关的资源.

那么很容易就会发现,如果一个interpreter进程只能有一个线程来执行, 多线程的并发则成为不可能, 即使这几个线程之间不存在资源的竞争.

从理论上讲,我们要尽可能地使程序更加并行, 能够充分利用多核的功能, 那么Python为什么要使用 全局的 GIL 来限制这种并行呢?

这个问题,其实已经得到了很多的讨论, 不止十年, 可以参考下面的文档:

反对 GIL 的声音:

  • An open letter to Guido van Rossum (这个文章值得一看,下面有很多的留言也值得一看)

认为 GIL 不能去除的:

  • It isn't Easy to Remove the GIL (这个文章来自python作者 Guido, 他说明了什么要使用 GIL)

其它的一些讨论很容易从Google来搜索得到, 譬如: GIL at google.

那么,简单总结下双方的观点.

认为应该去除 GIL 的:

  • 不顺应计算机的发展潮流(多核时代已经到来, 而 GIL 会很影响多核的使用)
  • 大幅度提升多线程程序的速度

认为不应该去除 GIL 的(如果去掉,会):

  • 写python的扩展(module)时会遇到锁的问题,程序员需要繁琐地加解锁来保证线程安全
  • 会较大幅度地减低单线程程序的速度

后者是 Guido 最为关切的, 也是不去除 GIL 最重要的原因, 一个简单的尝试是在1999年(十年前), 最终的结果是导致单线程的程序速度下降了几乎2倍.

归根结底,其实就是多进程与多线程的选择问题, 有一段话比较有意思, 可以参考 http://www.artima.com/forums/flat.jsp?forum=106&thread=214235.

我引用如下:

I actually don't think removing the GIL is a good solution.
But I don't think threads are a good solution, either.
They're too hard to get right, and I say that after spending literally years studying threading in both C++ and Java.
Brian Goetz has taken to saying that no one can get threading right.

引自 Bruce Eckel 对 Guido 的回复. 而 Bruce Eckel 是何许人, 如果你了解 java 或者 C++, 那么应该不会不知道他.

个人的观点

那么,从我自己的角度来看(我没有太多的多线程编程经验), 先不论多线程的速度优势等,我更加喜欢多进程的是:

  • 简单,无需要人为(或者语言级别)的加解锁. 想想 java 中的多线程编程,程序员通常会在此处出错(java程序员可以思考下)
  • 安全, 这也是浏览器为什么开始使用多进程的一个原因

依照Python自身的哲学, 简单 是一个很重要的原则,所以, 使用 GIL 也是很好理解的.

当然你真的需要充分利用多核的速度优势,此时python可能并非你最佳的选择,请考虑别的语言吧,如 java, erlang 等.

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python import自定义模块方法
Feb 12 Python
使用SAE部署Python运行环境的教程
May 05 Python
利用Python获取赶集网招聘信息前篇
Apr 18 Python
Python极简代码实现杨辉三角示例代码
Nov 15 Python
Python可变参数用法实例分析
Apr 02 Python
matplotlib在python上绘制3D散点图实例详解
Dec 09 Python
Python/ArcPy遍历指定目录中的MDB文件方法
Oct 27 Python
python GUI库图形界面开发之PyQt5滚动条控件QScrollBar详细使用方法与实例
Mar 06 Python
解决django无法访问本地static文件(js,css,img)网页里js,cs都加载不了
Apr 07 Python
Django 如何使用日期时间选择器规范用户的时间输入示例代码详解
May 22 Python
如何快速理解python的垃圾回收机制
Sep 01 Python
详解Selenium-webdriver绕开反爬虫机制的4种方法
Oct 28 Python
pygame实现贪吃蛇游戏(下)
Oct 29 #Python
python TK库简单应用(实时显示子进程输出)
Oct 29 #Python
pygame实现贪吃蛇游戏(上)
Oct 29 #Python
利用Python小工具实现3秒钟将视频转换为音频
Oct 29 #Python
pygame实现打字游戏
Feb 19 #Python
Python 实现自动导入缺失的库
Oct 29 #Python
python实现的读取网页并分词功能示例
Oct 29 #Python
You might like
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
用libtemplate实现静态网页生成
2006/10/09 PHP
discuz 首页四格:最新话题+最新回复+热门话题+精华文章插件
2007/08/19 PHP
PHP 中关于ord($str)>0x80的详细说明
2012/09/23 PHP
浅析Yii中使用RBAC的完全指南(用户角色权限控制)
2013/06/20 PHP
php一维二维数组键排序方法实例总结
2014/11/13 PHP
PHP使用Pthread实现的多线程操作实例
2015/11/14 PHP
php基于ob_start(ob_gzhandler)实现网页压缩功能的方法
2017/02/18 PHP
thinkphp实现附件上传功能
2017/05/26 PHP
PHP实现求连续子数组最大和问题2种解决方法
2017/12/26 PHP
PHP定义字符串的四种方式详解
2018/02/06 PHP
php对微信支付回调处理的方法
2018/08/23 PHP
javascript 控制弹出窗口
2007/04/10 Javascript
javascript XML数据显示为HTML一例
2008/12/23 Javascript
JavaScript探测CSS动画是否已经完成的方法
2016/08/30 Javascript
微信小程序 火车票查询实例讲解
2016/10/17 Javascript
Angular学习笔记之集成三方UI框架、控件的示例
2018/03/23 Javascript
Vue中如何实现proxy代理
2018/04/20 Javascript
JavaScript中的Proxy对象
2020/11/27 Javascript
[01:14:19]NAVI vs Mineski 2019国际邀请赛淘汰赛 败者组BO1 8.20.mp4
2020/07/19 DOTA
[05:06]DOTA2-DPC中国联赛 正赛 VG vs Magma选手采访
2021/03/11 DOTA
Python实现过滤单个Android程序日志脚本分享
2015/01/16 Python
python使用selenium登录QQ邮箱(附带滑动解锁)
2019/01/23 Python
Python实现读取txt文件中的数据并绘制出图形操作示例
2019/02/26 Python
python实现按首字母分类查找功能
2019/10/31 Python
Python导入数值型Excel数据并生成矩阵操作
2020/06/09 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
2020/12/04 Python
python绘制汉诺塔
2021/03/01 Python
Lululemon加拿大官网:加拿大知名体育服装零售商
2019/04/12 全球购物
Java程序员面试题
2013/07/15 面试题
粗加工管理制度
2014/02/04 职场文书
2014年营业员工作总结
2014/11/18 职场文书
2014年销售经理工作总结
2014/12/01 职场文书
岳麓书院导游词
2015/02/03 职场文书
2019幼儿教师求职信(3篇)
2019/09/20 职场文书
如何解决php-fpm启动不了问题
2021/11/17 PHP