Python GIL 一文全知道!
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 成为可选是什么看法呢?
相关文章:

Python GIL 一文全知道!
GIL 作为 Python 开发者心中永远的痛,在最近即将到来的更新中,终于要彻底解决了,整个 Python 社群都沸腾了 什么是GIL? GIL是英文学名global interpreter lock的缩写,中文翻译成全局解释器锁。GIL需要解决的是线程竞…...

数据库级别的MD5加密(扩展)
首先,我们要知道什么是MD5? 1.主要是增强算法的复杂性和不可逆性 2.MD5不可逆,具体的值MD5是一样的 3.MD5破解网站的原理,背后有一个字典 代码案例: -- 加密 update testMD5 set pwdmd5(pwd) where id1; update testMD5 set…...

Docker安装Jenkins,配置Maven和Java
前言 这是一个java的springboot项目,使用maven构建 安装准备 需要将maven和jdk安装在服务器上,Jenkins需要用到,还有创建一个jenkins的目录,安装命令如下: docker run -d -uroot -p 9095:8080 -p 50000:50000 --n…...
游戏分组(100用例)C卷 (JavaPythonC语言C++Node.js)
部门准备举办一场王者荣耀表演赛,有10名游戏爱好者参与,分为两队,每队5人。 每位参与者都有一个评分,代表着他的游戏水平。为了表演赛尽可能精彩,我们需要把10名参赛者分为实力尽量相近的两队。一队的实力可以表示为这一队5名队员的评分总和。 现在给你10名参与者的游戏水…...
python函数装饰器保存信息
1 python函数装饰器保存信息 python函数装饰器,可以通过实例属性、全局变量、非局部变量和函数属性,来保存被装饰函数的状态信息。 1.1 统计调用并跟踪 描述 通过装饰器统计函数调用次数,并且用打印来跟踪调用记录。 此装饰器用类的__ca…...
AI真正的Killer App 仍然缺席
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

Docker 镜像以及镜像分层
Docker 镜像以及镜像分层 1 什么是镜像2 Docker镜像加载原理2.1 UnionFs:联合文件系统2.2 Docker镜像加载原理2.3 Docker镜像的特点 3 镜像的分层结构4 可写的容器层 1 什么是镜像 镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行…...
aigc 启动器 sd-webui-aki-v4 decode_base64_to_file
下载地址: SD-WebUI启动器 绘世-启动器 | 万物档案 decode_base64_to_file报错: File "E:\BaiduNetdiskDownload\stable diffusion\sd-webui-aki-v4\extensions\sd-webui-controlnet\scripts\external_code.py", line 7, in <module>fr…...

【C++进阶05】AVL树的介绍及模拟实现
一、AVL树的概念 二叉搜索树的缺点 二叉搜索树虽可以缩短查找效率 但如果数据有序或接近有序 二叉搜索树将退化为单支树 查找元素相当于在顺序表中搜索元素,效率低下 AVL树便是解决此问题 向二叉搜索树中插入新结点 并保证每个结点的左右子树 高度之差的绝对值不超…...

MySQL视图 索引 面试题
一. 视图 视图:一种虚拟存在的表,行和列的数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的,只保存了sql逻辑,不保存查询结果 视图语法 -- 创建 create view 视图名 as 查询语句;-- 使用 select * f…...

JAVA实现文件上传至阿里云
注册阿里云账号后,开通好对象存储服务(OSS),三个月试用 阿里云登录页 (aliyun.com) 目录 一.创建Bucket 二.获取AccessKey(密钥) 三.参考官方SDK文件,编写入门程序 1.复制阿里云OSS依赖,粘贴…...

设计模式之外观模式【结构型模式】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档> 学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某…...

Qt QCheckBox复选按钮控件
文章目录 1 属性和方法1.1 文本1.2 三态1.3 自动排他1.4 信号和槽 2 实例2.1 布局2.2 代码实现 Qt中的复选按钮类是QCheckBox它和单选按钮很相似,单选按钮常用在“多选一”的场景,而复选按钮常用在"多选多"的场景比如喜欢的水果选项中…...

加速科技ST2500 数模混合信号测试设备累计装机量突破500台!
国产数字机,测试中国芯!新年伊始,国产半导体测试设备领军企业加速科技迎来了振奋人心的一刻,ST2500 数模混合信号测试设备累计装机量突破500台!加速科技凭借其持续的创新能力、完善的解决方案能力、专业热忱的本地化服…...

ASP.NETCore WebAPI 入门 杨中科
ASP.NETCore WebAPI入门1 回顾 mvc开发模式 前端代码和后端代码是混在一个项目之中 WEB API 1、什么是结构化的Http接口。Json。 2、Web API项目的搭建。 3、Web API项目没有Views文件夹。 4、运行项目,解读代码结构。 5、【启用OpenAPI支持】→>swagger,在界…...
问题 C: 活动选择
题目描述 学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用其他教室。 现在给出n个活动使用礼堂的起…...

SpringBoot学习(五)-Spring Security配置与应用
注:此为笔者学习狂神说SpringBoot的笔记,其中包含个人的笔记和理解,仅做学习笔记之用,更多详细资讯请出门左拐B站:狂神说!!! Spring Security Spring Security是一个基于Java的开源框架,用于在Java应用程…...
Java解决删除子串后的字符串最小长度
Java解决删除子串后的字符串最小长度 01 题目 给你一个仅由 大写 英文字符组成的字符串 s 。 你可以对此字符串执行一些操作,在每一步操作中,你可以从 s 中删除 任一个 "AB" 或 "CD" 子字符串。 通过执行操作,删除所…...

日志系统一(elasticsearch+filebeat+logstash+kibana)
目录 一、es集群部署 安装java环境 部署es集群 安装IK分词器插件 二、filebeat安装(docker方式) 三、logstash部署 四、kibana部署 背景:因业务需求需要将nginx、java、ingress日志进行收集。 架构:filebeatlogstasheskib…...

游戏版 ChatGPT,要用 AI 角色完善生成工具实现 NPC 自由
微软与 AI 初创公司 Inworld 合作,推出基于 AI 的角色引擎和 Copilot 助理,旨在提升游戏中 NPC 的交互力和生命力,提升游戏体验。Inworld 致力于打造拥有灵魂的 NPC,通过生成式 AI 驱动 NPC 行为,使其动态响应玩家操作…...

【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...

(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

使用LangGraph和LangSmith构建多智能体人工智能系统
现在,通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战,比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...
JavaScript 数据类型详解
JavaScript 数据类型详解 JavaScript 数据类型分为 原始类型(Primitive) 和 对象类型(Object) 两大类,共 8 种(ES11): 一、原始类型(7种) 1. undefined 定…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...