Python GIL 一文全知道!

news2024/11/16 15:59:12

GIL 作为 Python 开发者心中永远的痛,在最近即将到来的更新中,终于要彻底解决了,整个 Python 社群都沸腾了

什么是GIL?

GIL是英文学名global interpreter lock的缩写,中文翻译成全局解释器锁。GIL需要解决的是线程竞争(thread racing)的问题,所以需要理解GIL的作用机制,我们需要了解一些简单的背景知识,包括程序在计算机上运行的机制,线程和进程的区别,还有并行和并发的概念。

程序在计算机上是如何运行的?

我们的程序代码是存储在硬盘上,当启动程序时,cpu会为应用程序启动进程(process),小应用程序只需要一条进程,但大的应用程序如Google Chrome在后台会启动多条进程,对应不同的服务。

每条进程都是独立的运行单元,计算机会为每条进程分配内存空间,并提供必要的计算资源。我们的程序代码在启动进程时会被加载到这个特定的内存区块里作为数据资源供计算机cpu调用。

为了更好地理解,这里我们可以把进程想象成高速路上的某条车道,那线程(thread)自然就是车道上跑的汽车。一个程序可以以单线程的方式运行,就像只有一辆车在车道上跑,当然也可以以多线程的方式运行,这就像很多车在同一车道上跑。

进程之间是相对独立运行的,而在同一进程上运行的线程之间是共享内存的。这是进程和线程最大的区别,这个区别意味着开启多个进程会消耗更多的内存,但开启多个线程不会额外增加内存负担。

另外,如果一条进程挂了,其它进程继续照常运行,但如果一条线程挂了,相应的进程也会挂掉,运行在该进程上的所有线程自然也会跟着挂掉。

这就好比,在高速路上,如果车道1(进程的比照)出现交通事故,那整条车道都将不能使用,但这并不影响其它车道(其它进程继续运行)的正常运行。同样的道理,如果在车道1上有一辆车(线程的比照)发生故障停在车道上不能动了(线程挂了),那其它车辆也不能行驶了,这条车道暂停服务进入交通管制状态(进程挂了),直到故障车被拉出车道,管制消除,车道继续畅通运行(进程重启)。

并行和并发的区别?

理解了进程和线程,并行和并发就很好理解了。

并行和并发是计算机处理任务的两种不同模式。

在并行模式(parallelism)下,多任务可以同时运行,提高了计算机的性能。这里要注意一点,要真正实现并行,程序必须在多核机器上运行,并行任务同时运行在不同的cpu上,在时间片轮上是可以重叠的,如下图中Task 1和Task 3。但我们来看Task 1和Task 2是跑在同一个cpu上的,而且它们是交替进行,这种模式就是并发模式。还有一点很重要,这个问题曾经让我困惑很久。那就是并行模式理论上可以通过多线程并行也可以通过多进程并行。

多进程并行就是在每个cpu上启动新的进程,每个进程都需要分配独立内存空间,而多线程并行是在每个cpu中启动新的线程,该线程共享主线程的内存空间。由于Python GIL的限制,在Python环境中,并行只能通过多进程的模式,而其它语言如Java,C等可以通过多线程的方式进行并行运算。

并发模式(concurrency)基于线程机制,计算机将多个任务分配给在同一进程中的不同线程进行运行,不过在并发模式下多线程不能同时处理任务,但也不是像单线程一样,一个接着一个处理,而是通过交替循环。如上图所示,假设有两个任务Task 1和Task 2分别运行在线程A和B上,在并发模式下,线程A运行一小段时间后cpu把它挂起,并切换到线程B,线程B又运行一小段时间后被挂起,同时切换回线程A,如此循环,直到任务完成。

在实践中,并行和并发不是非此即彼,根据任务的不同程序的运行可以被合理配置,最大程度地利用计算机上的cpu资源。还是用上面的图来举例,我们将任务分成两个平行的任务单元,并将这两个任务单元分配给两个cpu进行并行运算,同时在每个cpu里,任务单位再细分为两个更小的任务(Task 1和Task 2,以及Task 3 和Task 4),这两个小任务将在各个cpu里两个线程中并发运行。

除了这种任务配置模式,还可以有其它的配置方案,比如可以让每个任务单元在某些cpu上进行单线程运行,在某些cpu上开多线程并发运行,这取决于可支配的cpu资源、需要完成的任务的类型以及任务之间的耦合性,需要具体问题具体分析。

那并行和并发运行模式分别适合于什么样的场景呢?

首先计算机所处理的任务大致可以分为计算密集型和IO密集型任务。

计算密集型顾名思义需要大量的cpu算力,比如人工智能模型里大型矩阵运算,图形处理等;而IO密集型任务,是指磁盘IO、网络IO占主要的任务,计算量很小,不需要消耗太多计算资源,大部分在等待的状态,比如向服务器请求数据,线程要等待服务器的响应。

如果我们的任务都是计算密集型的,通过多线程并发模式运行程序并不能带来性能的提高,可能反而比单线程下所花的时间更长,因为首先并发不是同时处理任务,而是短时轮流循环执行子任务,并且在线程切换时需要额外消耗计算机资源,这样情况适合多任务并行运算。

如果计算机需要处理的是IO密集型任务,这时如果用并行模式,我们会浪费很多cpu资源,因为处理IO密集型任务不需要太多计算资源,大部分时间是在等待,所以这种情况适合用单cpu并发模式,好处是只用一个cpu资源,通过交替执行任务,可以在IO任务中的等待时间区间里切换线程去执行其它IO任务,从而更充分地利用了cpu资源。

还有一种情况,如果我们需要处理的任务既有计算密集型任务又有IO密集型任务,那我们可以考虑同时用并行和并发,在分配任务单元时,将计算密集型和IO密集型任务进行混合,如此单个cpu上既有有计算密集型又有IO密集型任务,那采用多线程并发就会极大地提高计算机运行效率,原因是计算机在处理IO密集任务时不需要浪费等待的时间,在IO阻塞的情况,切换线程到计算密集任务,这就用IO等待时间来处理其它计算密集型任务,从而提高程序运行效率。

Python语言中的GIL

Python的解释器是CPython,CPython本身并不确保线程安全(thread safe),也就是解释器不会对多个线程对同一个python对象的操作行为进行约束。这里我们来举个例子。在这个例子中,我们考虑两个线程(1和2)共享同一个变量a,a的初始值为1。我们可以允许两个线程以任何顺序对变量a进行两种写操作,其中,在线程1中,我们修改a的值 a= a+2 ,在线程2中,我们也修改a的值 a = a*2 。取决于两个写操作的顺序,我们可以有以下几种不同情况。

情况一, a= a+2 —> a = a*2 a的最终值为6

情况二, a= a * 2 —> a = a+2 这种情况下的a的最终值为4

情况三,线程1和线程2同时获取变量a,那结果就取决于哪条线程最后修改a的值,如果线程1后修改a的值,那a的最终值就是3,如果线程2后修改a的值,那a的最终值是2。

这三种情况都有可能发生,所以每次运行程序前我们都无法预知a的最终值,这是由于线程竞争带来的side effect,虽然程序员在正常情况下都不会去做线程不安全的操作,即两个线程可以同时以任何顺序修改一个共有变量,但如果对多线程的任务执行顺序不加限制,这种错误在理论上是可能发生的,特别是当业务代码量很大又有多个程序员同时维护开发代码时,犯这种错误的可能性变大。

要避免这种线程不安全的编程行为,要么对程序员的编程行为进行约束(比如Java中有多线程并行编程的一系列安全规范),要么在编程语言层面上设计一种机制杜绝这种不安全的线程行为,Python中的GIT就是提供了这种机制。

GIT的概念很简单,GIT是个全局变量,被所有线程共享。并且,GIT在特定的时刻只允许被一条线程获取,获取GIT锁的线程就拥有执行任务的权限,而其它线程必须先获取GIT锁才可以执行线程任务。

这样一来,Python就断了多线程并行的路子。在Python里,多线程只能并发。如果需要并行,只能通过多进程的模式,具体实现通过内置库multiprocessing。

删除 GIL

现在,Python 团队已经正式接受了删除 GIL 的这个提议,并将其设置为可选模式,可谓是利好广大开发者。

做出这一贡献的是一位来自 Meta 的名叫 Sam Gross 的软件工程师,他花费了四年多的时间才完成这一工程。

在得知这一消息后,大家纷纷叫好,深度学习三巨头之一的 Yann LeCun 发文祝贺:没有了 GIL,现在,Python 代码可以自由的执行多线程了。

CPython 核心开发者 Thomas Wouters 撰文描述了 Python 中的无 GIL 细节,并对未来发展做了展望。

原文翻译如下:

非常感谢所有人对无 GIL 提议的反馈,整体上都持积极的支持态度。指导委员会打算接受无 GIL 提议,并就以下具体细节与大家分享。

我们的基本设想是:

  • 长期来看(大约 5 年以上),no-GIL 构建应是唯一的构建;
  • 我们希望非常谨慎地对待向后兼容。我们不希望出现另一个 Python 3 的情况,所有适应 no-GIL 构建所需的第三方代码更改应只适用于 with-GIL 构建(尽管仍要解决更老 Python 版本的向后兼容性问题)。这不适用于 Python 4。我们仍在考虑对这两个构建的 ABI 兼容性和其他细节的要求,以及对向后兼容性的影响;
  • 在我们承诺完全转向 no-GIL 之前,希望看到社区的支持。我们不能只是更改默认设置,更希望社区弄清自己做什么工作来给予我们支持。我们核心开发团队需要获得新构建模式及相关所有内容的经验。我们要整理现有代码中的线程安全性,需要弄明白新的 C API 和 Python API。我们在获得这些洞见时还需要传达给 Python 社区的其他人,并确保自身想要做出的更改以及希望其他人做出的更改是可取的;
  • 在我们默认 no-GIL 设置之前的任何时候,如果事实证明了,它的破坏性太大导致收益太少,我们希望能够改变主意。这也就意味着我们会回滚所有工作,因此在我们确定要将 no-GIL 设为默认方式之前,特定于 no-GIL 的代码在某种程度上应是可识别的。

目前,我们认为未来的道路分为以下三个阶段:

  • 短期内,我们会将 no-GIL 构建作为一种实验性构建模式,大概会在 3.13 版本(也有可能推迟到 3.14 版本)可用。之所以是实验性的,是因为我们核心开发团队虽然支持这一构建模式,但不期望整个社区都会支持它。我们需要时间理清自己要做什么,至少在 API 设计以及打包和分发方面,从而得到社区的支持。我们也不鼓励 distributor 将实验性 no-GIL 构建作为默认解释器发布。
  • 中期来看,在我们确信得到足够的社区支持并使 no-GIL 的生产使用可行后,我们将支持 no-GIL 构建,但不是默认方式,而是在某个目标日期或某个 Python 版本中使它成为默认方式。具体的时间将取决于很多因素,比如 API 更改最终的兼容性如何、社区认为他们仍然需要做多少工作等。我们预计这至少需要一至两年的时间。一旦我们宣布支持,预计将有一些 distributor 会开始默认发布 no-GIL。
  • 长期来看,我们希望 no-GIL 成为默认方式,并删除 GIL 的所有痕迹(但不会不必要地破坏向后兼容性)。我们不希望等太长时间,毕竟两种常用的构建模式同时存在会给社区造成很大的负担(比如需要双倍测试资源和 debug 场景)。但是我们也不能急于求成。我们认为这一过程将需要花费五年的时间。

当然在整个过程中,我们整个开发团队将需要实时评估进程并对时间线进行调整。

评论区的小伙伴们,你们对 GIL 成为可选是什么看法呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1372304.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

遥感影像-语义分割数据集:云数据集详细介绍及训练样本处理流程

原始数据集详情 简介:该云数据集包括150张RGB三通道的高分辨率图像,在全球不同区域的分辨率从0.5米到15米不等。这些图像采集自谷歌Earth的五种主要土地覆盖类型,即水、植被、湿地、城市、冰雪和贫瘠土地。 KeyValue卫星类型谷歌Earth覆盖区…

太惨了,又一个程序员被渣的开年大瓜

今天闲暇之余浏览了一下mm,忽然看见一条瓜:某东pdf瓜,一份19页的PDF文件,题为《婚房变赠予,京东渣女出轨连环套设计冤大头程序员》,点进去看了一下,简直炸裂了三观,男同志们一定要保…

EI级 | Matlab实现VMD-TCN-LSTM变分模态分解结合时间卷积长短期记忆神经网络多变量光伏功率时间序列预测

EI级 | Matlab实现VMD-TCN-LSTM变分模态分解结合时间卷积长短期记忆神经网络多变量光伏功率时间序列预测 目录 EI级 | Matlab实现VMD-TCN-LSTM变分模态分解结合时间卷积长短期记忆神经网络多变量光伏功率时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.【E…

【LLM 论文阅读】NEFTU N E: LLM微调的免费午餐

指令微调的局限性 指令微调对于训练llm的能力至关重要,而模型的有用性在很大程度上取决于我们从小指令数据集中获得最大信息的能力。在本文中,我们提出在微调正向传递的过程中,在训练数据的嵌入向量中添加随机噪声,论文实验显示这…

彭博评选2024年50家企业,比亚迪、联发科上榜 | 百能云芯

彭博资讯于9日发布2024年全球50家值得关注的企业名单,该名单由彭博分析师团队从金融到食品等领域追踪了约2,000家企业中挑选出的,根据「观点聚焦」清单,选出50家值得关注的公司,重点考虑了其独特观点、领导层变化、资产出售或并购…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷①

2023年全国职业院校技能大赛(高职组) “云计算应用”赛项赛卷1 目录 需要竞赛软件包环境以及备赛资源可私信博主!!! 2023年全国职业院校技能大赛(高职组) “云计算应用”赛项赛卷1 模块一 …

Flask 菜品管理

common/libs/Helper.py getDictFilterField() 方法 用于在web/templates/food/index.html中展示菜品分类 如何能够通过food里面的cat_id获取分类信息呢?只能通过for循环,这样会很麻烦,所以定义了这个方法。 这个方法可以的查询返回结果…

【数据库系统概论】期末复习1

试述数据、数据库、数据库系统、数据库管理系统的概念。试述文件系统与数据库系统的区别和联系。试述数据库系统的特点。数据库管理系统的主要功能有哪些?试述数据库系统三级模式结构,这种结构的优点是什么?什么叫数据与程序的物理独立性&…

玩转QrCode

生成二维码,跳转指定 url 导入模块 npm install --save qrcode.vue1.7.0 引入模块 import QrcodeVue from qrcode.vue编写页面 button 触发 <template><el-button type"primary" click"showQRCode"><svg-icon icon-class"code&quo…

如何在IEC61850的ICD文件中添加新的DO节点

写在前面 恭喜“梅山剑客”粉丝突破1K&#xff0c;为了纪念这一伟大的时刻&#xff0c;今日发表此文&#xff0c; 纪念这神圣的时间节点&#xff0c;愿各位 青春永驻&#xff0c;笔耕不息。 本文参考链接&#xff1a; 1、61850开发知识总结与分享 2、IEC61850建模说明 1 简介…

ELF文件格式解析二

使用objdump命令查看elf文件 objdump -x 查看elf文件所有头部的信息 所有的elf文件。 程序头部&#xff08;Program Header&#xff09;中&#xff0c;都以 PT_PHDR和PT_INTERP先开始。这两个段必须在所有可加载段项目的前面。 从上图中的INTERP段中&#xff0c;可以看到改段…

QT第三天

完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后&#xff0c;关闭登录界面&#xff0c;跳转到其他界面如果账号和密码不匹配&#xff0…

内存淘金术:Redis 内存满了怎么办?

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 内存淘金术&#xff1a;Redis 内存满了怎么办&#xff1f; 前言LRU&#xff08;Least Recently Used&#xff09;算法LFU&#xff08;Least Frequently Used&#xff09;算法定期淘汰策略内存淘汰事件…

最新出炉!知乎最牛最全JMeter+Ant+Jenkins接口自动化测试框架(Windows)

一:简介 大致思路&#xff1a;Jmeter可以做接口测试&#xff0c;也能做压力测试&#xff0c;而且是开源软件&#xff1b;Ant是基于Java的构建工具&#xff0c;完成脚本执行并收集结果生成报告&#xff0c;可以跨平台&#xff0c;Jenkins是持续集成工具。将这三者结合起来可以搭…

JNPF低代码是什么,怎么样?

低代码开发平台已经是程序员日常标配效率工具了&#xff0c;曾经我们拿到开发需求后&#xff0c;要先配 Vue & React 等框架环境、再选需要引入的第三方组件库&#xff0c;最后穿针引线&#xff0c;调试搭建起需要的后台工具。这其中无数深坑等着我们去趟&#xff0c;直到低…

【C++进阶05】AVL树的介绍及模拟实现

一、AVL树的概念 二叉搜索树的缺点 二叉搜索树虽可以缩短查找效率 但如果数据有序或接近有序 二叉搜索树将退化为单支树 查找元素相当于在顺序表中搜索元素&#xff0c;效率低下 AVL树便是解决此问题 向二叉搜索树中插入新结点 并保证每个结点的左右子树 高度之差的绝对值不超…

企业邮箱性价比之选:服务、功能与价格的实用指南

企业选择企业邮箱服务商时需要考虑的几个核心指标&#xff1a;稳定性、安全性、容量大小、用户体验以及价格因素。只有在这些方面都有良好表现的邮箱服务商&#xff0c;才能称得上是性价比高的选择。 一、企业邮箱选择参考指标 1、稳定性 稳定性是企业邮箱服务的生命线&#xf…

【Python】编程练习的解密与实战(一)

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《Python | 编程解码》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1fa90;1. 初识Python &a…

vue3 组合式 API 在 onMounted 中调用 dom 报错 Initialize failed: invalid dom.

问题 在开发的过程中&#xff0c;项目中需要用到 echarts&#xff0c;引入后在渲染的过程中报错了&#xff1a;Initialize failed: invalid dom. 这个报错表示元素在未渲染完成的情况下就被调用了&#xff0c;作者在以前也遇到过这种情况&#xff0c;在 vue2 中正常来说将 ech…

深入理解 Flink(一)Flink 架构设计原理

大数据分布式计算引擎设计实现剖析 MapReduce MapReduce 执行引擎解析 MapReduce 的组件设计实现图 Spark 执行引擎解析 Spark 相比于 RM 的真正优势的地方在哪里&#xff1a;&#xff08;Simple、Fast、Scalable、Unified&#xff09; DAG 引擎中间计算结果可以进行内存持…