对 Python 中 GIL 的理解

news/2024/9/20 10:48:52/ 标签: python, GIL

GIL_0">一.Python 中的 GIL

Python 中的全局解释器锁(Global Interpreter Lock,GIL)是 CPython 解释器的一个机制,用来确保在多线程环境下,只有一个线程可以执行 Python 字节码,任何时刻只能有一个线程在执行 Python 代码。这意味着,即使有多个 CPU 核心,Python 的多线程程序在同一时间也只有一个线程在执行,无法充分利用多核处理器的优势。

多核处理器是指在同一个物理处理器芯片上集成了多个独立的处理核心(也称为"核")。每个核心都可以独立执行指令,具备自己的运算单元、寄存器等,从而允许同时处理多个任务或线程。多核处理器的出现是为了应对单核处理器频率提高所带来的功耗和散热问题,同时显著提升计算性能。简单理解,多核处理器中的核指的就是逻辑 CPU。

GIL__6">1.GIL 存在原因

GIL 设计初衷是为简化 CPython 解释器中对内存管理处理。在 Python 中内存管理使用引用计数机制(reference counting)。GIL 确保了内存管理的线程安全性,避免了多线程环境下的内存管理问题,如内存泄漏或数据竞争。

GIL__10">2.GIL 的影响

(1)多线程并发受限

由于 GIL 的存在,即使开启了多线程,Python 程序在 CPU 密集型任务(如大量计算)中也不能真正实现多线程并发。所有线程必须轮流获取 GIL,这导致多线程程序的性能提升有限甚至可能变差。

(2)I/O 密集型任务

对于 I/O 密集型任务(如文件读写、网络请求),GIL 的影响较小。因为 I/O 操作通常会等待系统响应,Python 可以在一个线程等待 I/O 的同时释放 GIL,允许其他线程运行,因此在 I/O 密集型任务中多线程仍然有优势。

GIL_20">3.如何绕过 GIL

虽然 GIL 在 CPython 中存在限制,但有几种方法可以绕过或减少其影响。比如,多进程(multiprocessing)、使用其它解释器(Jython|IronPython)、C 扩展模块、多线程 +I/O 密集型任务。

GIL__24">二.GIL 与多进程

GIL(全局解释器锁)与多进程是两个不同的概念,但它们在多核计算中的应用息息相关,尤其在 Python 中。

GIL__28">1.GIL 与多进程关系

在 Python 中,GIL 限制了多线程的性能,但多进程可以通过为每个进程分配独立的 GIL 来解决这个问题。通过使用多进程模型,程序可以充分利用多核处理器,实现真正的并行计算。

2.多进程的实现

Python 提供了 multiprocessing 模块,使得在 Python 中使用多进程编程变得相对简单。通过 multiprocessing,可创建多个进程,每个进程独立运行,不共享内存,能够绕过 GIL 并实现并行计算。

python">from multiprocessing import Processdef cpu_bound_task(n):result = 0for i in range(n):result += i * iprint(f"Result: {result}")if __name__ == "__main__":processes = []for _ in range(4):  # 创建四个进程p = Process(target=cpu_bound_task, args=(10000000,))processes.append(p)p.start()for p in processes:p.join()  # 等待所有进程完成

在这个例子中,cpu_bound_task 是一个 CPU 密集型任务,使用了 multiprocessing.Process 来创建多个进程,这些进程可以在不同的 CPU 核心上并行运行,避免了 GIL 的限制。

3.多进程的优缺点

(1)优点

  • 绕过 GIL 限制:每个进程都有独立的 GIL,能够在多核 CPU 上并行执行多个进程,显著提升 CPU 密集型任务的性能。
  • 进程隔离:每个进程拥有独立的内存空间,进程之间不会相互影响,这提高了安全性。

(2)缺点

  • 进程开销:进程比线程更重,每个进程都有独立的内存空间,因此进程的启动和内存占用成本更高。
  • 进程间通信复杂:由于进程之间不共享内存,必须使用进程间通信(如管道、队列、共享内存)来交换数据,这比线程间的通信更复杂。
  • 内存消耗大:因为每个进程都有独立的内存空间,处理大量数据时可能会占用更多的内存。

4.使用场景

(1)多线程适合 I/O 密集型任务

由于 I/O 操作(如网络请求、文件操作)会释放 GIL,因此在这些场景中使用多线程能够有效提高效率。

(2)多进程适合 CPU 密集型任务

对于需要大量计算的任务,如图像处理、数据分析和科学计算,使用多进程可以绕过 GIL,充分利用多核 CPU,提升性能。

GIL__81">三.GIL 与多线程

GIL(全局解释器锁)和多线程在 Python 中的关系是编写并发程序时需要特别注意的关键点。尽管 Python 支持多线程编程,但由于 GIL 的存在,多线程在 Python 中的性能在某些情况下受到了限制。

GIL__85">1.GIL 与多线程的限制

GIL 的约束下,Python 的多线程实现的效率在以下场景中受到限制:

(1)CPU 密集型任务

进行大量数学计算或图像处理等需要 CPU 大量计算的任务。由于 GIL 的存在,Python 的多个线程在这种情况下并不能真正并行执行,而是轮流获取 GIL 执行任务,因此不能充分利用多核处理器的优势。

(2)频繁切换 GIL

多线程程序中,线程频繁获取和释放 GIL 会导致线程间的上下文切换,这带来了额外的开销,反而可能降低整体程序的性能。

GIL__IO__97">2.GIL 对 I/O 密集型任务影响

虽然 GIL 对 CPU 密集型任务限制很大,但对 I/O 密集型任务的影响较小。在进行 I/O 操作(如文件读写、网络请求)时,线程会等待外部资源的响应,而此时线程会释放 GIL,使得其它线程可以运行。因此,I/O 密集型任务如网络爬虫、数据库操作等,多线程仍然能显著提高效率。

GIL__101">3.GIL 下有效使用多线程

(1)I/O 密集型任务

对于需要频繁进行 I/O 操作的任务,如网络请求、文件操作等,多线程能够有效地提升程序的并发性。在这些任务中,线程在等待 I/O 时会释放 GIL,其它线程可以执行。

python">import threading
import timedef io_task():print("Starting I/O task")time.sleep(2)  # 模拟I/O操作print("I/O task finished")threads = []
for i in range(4):t = threading.Thread(target=io_task)threads.append(t)t.start()for t in threads:t.join()

(2)使用外部库

对于 CPU 密集型任务,可以借助使用 C/C++ 实现的外部库,如 NumPy、SciPy 等。因为这些库的大量计算部分是用 C 语言编写的,它们可以在不受 GIL 限制的情况下并行执行。GIL 只在执行 Python 字节码时才有效,但在执行 C 扩展模块时可以被释放。

(3)多进程替代多线程

对于 CPU 密集型任务,可以考虑使用多进程而不是多线程。每个进程都有独立的 GIL,并且可以运行在不同的 CPU 核心上,从而真正实现并行计算。

python">from multiprocessing import Processdef cpu_task():total = 0for i in range(10000000):total += iprint(total)processes = []
for i in range(4):p = Process(target=cpu_task)processes.append(p)p.start()for p in processes:p.join()

(4)异步编程

除了多线程,Python 还支持异步编程(asyncio),尤其适用于 I/O 密集型任务。异步编程通过事件循环和协程来管理任务的并发执行,避免了线程上下文切换的开销和 GIL 的影响。

python">import asyncioasync def async_task():print("Starting async task")await asyncio.sleep(2)  # 模拟异步I/O操作print("Async task finished")async def main():tasks = [async_task() for _ in range(4)]await asyncio.gather(*tasks)asyncio.run(main())

参考文献

[1] https://docs.python.org/zh-cn/3/library/asyncio.html

[2] https://docs.python.org/zh-cn/3/library/multiprocessing.html

[3] No GIL Python 的冒险:https://www.4async.com/2024/03/adventure-of-no-gil-python/

[4] Python 的 GIL 是什么鬼,多线程性能究竟如何:https://cenalulu.github.io/python/gil-in-python/

[5] Understanding the Python GIL:https://www.dabeaz.com/GIL/

NLP工程化(星球号)


http://www.ppmy.cn/news/1527959.html

相关文章

TopoDOT2024.1注册机 道路自动化提取 雷达点云数据

TopoDOT2024.1是一套成熟的点云数据处理及应用系统,全面具备点云数据的存储管理、精度检核、特征自动提取、智能分析、高效建模、成果输出等应用功能。TopoDOT在LiDAR数据应用领域有着多年的实战经验,用户在实际项目中长期使用,尤其在交通领域…

如何利用 Visual Studio 和 AI 工具实现高效编程

哪个编程工具让你的工作效率翻倍? 在现代软件开发的世界中,编程效率的提升对开发者来说至关重要。高效编程不仅仅是编写更多的代码,还包括如何减少重复劳动、提高代码质量、加快调试和测试流程等。而 Visual Studio 作为一个功能强大的开发环境(IDE),配合各种 AI 工具,…

JAVA-期末成绩计算

要求 总评成绩 期末成绩*0.6 平时成绩*0.4 输入总评成绩和平时成绩&#xff0c;输出期末成绩要考几分&#xff08;0<平时成绩<40&#xff0c;0<总评成绩<100&#xff09; 要求能多次运行&#xff08;退出程序停止&#xff09; 代码 import java.util.Scanne…

灾备技术演进之路 | 虚拟化无代理备份只能挂载验证和容灾吗?只能无代理恢复吗?且看科力锐升级方案

灾备技术演进之路系列 虚拟化备份技术演进 摆脱束缚&#xff0c;加速前行 无代理备份仅能挂载/恢复验证吗&#xff1f; ——科力锐极简验证演练无代理备份来了 无代理备份无法应对平台级故障吗&#xff1f; ——科力锐应急接管无代理备份来了 无代理备份仅能同平台挂载吗&a…

AWS 实时数据流服务 Kinesis

AWS 实时数据流服务 Kinesis 什么是 KinesisKinesis 数据来源示例 AWS Lambda 发送数据到 Kinesis步骤 1&#xff1a;创建 Kinesis 数据流步骤 2&#xff1a;编写 Lambda 函数步骤 3&#xff1a;配置 Lambda 函数权限部署和测试 Lambda 函数 消费和处理 Kinesis 数据流示例 Fli…

英飞凌 PSoC6 RT-Thread 评估板简介

概述 2023年&#xff0c;英飞凌&#xff08;Infineon&#xff09;联合 RT-Thread 发布了一款 PSoC™ 62 with CAPSENSE™ evaluation kit 开发板 &#xff08;以下简称 PSoC 6 RTT 开发板&#xff09;&#xff0c;该开发套件默认内置 RT-Thread 物联网操作系统。PSoC 6 RTT 开…

会计稳健性Cscore模型(2000-2022年)

参考文献 甄红线, 王三法, 王晓洪. (2019). 公司债特殊条款、债券评级与会计稳健性. 会计研究, (10), 42-49.王晓亮, 蒋勇, 刘振杰. (2019). 董事会断裂带、会计稳健性与真实盈余管理. 审计研究, (05), 120-128. 会计稳健性&#xff0c;也称为会计保守性&#xff0c;是指企业…

PostgreSQL常用表操作SQL脚本整理

标题 查看 PostgreSQL表字段信息包括 名称&#xff0c;数据类型&#xff0c;精度&#xff0c;注释等信息查看数据库中的 schemas查看特定 schema 中的所有表查看指定表所有列的信息&#xff1a;查看主键信息&#xff1a;查看索引信息&#xff1a; 创建库表模板修改表信息修改表…

Vue.js 的 Mixins

Vue.js 的 Mixins 是一种非常强大且灵活的功能&#xff0c;它允许你封装可复用的 Vue 组件选项。Mixins 实际上是一种分发 Vue 组件可复用功能的非常灵活的方式。一个 mixin 对象可以包含任意组件选项。当组件使用 mixin 时&#xff0c;所有 mixin 选项将被“混入”该组件本身的…

切换淘宝最新镜像源npm

要切换淘宝的最新镜像源 npm&#xff0c;可以按照以下步骤进行操作&#xff1a; 安装 Node.js 和 npm&#xff1a; 确保你的系统中已经安装了 Node.js 和 npm。可以通过以下命令检查&#xff1a; node -v npm -v切换 npm 源&#xff1a; 使用以下命令将 npm 的源切换到淘宝镜像…

VUE工程中axios基本使用

安装axios npm install axios -s在main.js中引入 import http from axios Vue.prototype.$http = http将其绑定在VUE的prototype属性中 vue工程目录下,新建config文件夹,在config文件夹下新建index.js export default {

【Linux进程控制】进程程序替换

目录 进程程序替换 替换函数 看现象 替换原理 多进程替换 exec*函数使用&#xff08;部分&#xff09;&#xff0c;并且认识函数参数的含义 1.execl 2.execv 3.execvp 4.execvpe execlp 和execlpe 替换函数总结 进程程序替换 替换函数 有六种以exec开头的函数&am…

高德地图JS API加载行政区边界AMap.Polygon

&#x1f916; 作者简介&#xff1a;水煮白菜王 &#xff0c;一位资深前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 高德AMap专栏 &#xff0c;记录一下平时在博客写作中&#xff0c;总结出的一些开发技巧✍。 感谢支持&#x1f495;&#x1f495;&#x1f49…

PDP 和 ICE 图的终极指南

部分依赖图和单独条件期望图背后的直觉、数学和代码(R 和 Python) PDP 和 ICE 图都可以帮助我们了解我们的模型如何做出预测。 使用个人显示面板我们可以将模型特征和目标变量之间的关系可视化。它们可以告诉我们某种关系是线性的、非线性的还是没有关系。 同样,当特征之间…

Java 入门指南:JVM(Java虚拟机)—— Java 类加载器详解

类加载器 类加载器&#xff08;Class Loader&#xff09;是 Java 虚拟机&#xff08;JVM&#xff09;的一部分&#xff0c;它的作用是将类的字节码文件&#xff08;.class 文件&#xff09;从磁盘或其他来源加载到 JVM 中。类加载器负责查找和加载类的字节码文件&#xff0c;并…

通过防火墙分段增强网络安全

什么是网络分段‌ 随着组织规模的扩大&#xff0c;管理一个不断扩大的网络成为一件棘手的事情&#xff0c;同时确保安全性、合规性、性能和不间断的运行可能是一项艰巨的任务。为了克服这一挑战&#xff0c;网络管理员部署了网络分段&#xff0c;这是一种将网络划分为更小且易…

openssl-AES-128-CTR加解密char型数组分析

本文章通过对一个unsigned char*类型的数据做简单的加解密操作来学习如何使用openssl库函数。 openssl为3.0.0&#xff0c;对此前版本的很多函数都不兼容。 加解密源码 #include <openssl/evp.h> #include <openssl/err.h> #include <string.h> #include …

职场人生-外企福利待遇

今天来给大家分享一下外企的待遇&#xff0c;虽然外服享受不到这些待遇&#xff0c;但是也可以跟大家分享分享 1.四季福利.外企一般在春夏秋冬都会发印有公司logo的衣服&#xff0c;春天的外套&#xff0c;夏天的短袖&#xff0c;秋天的长袖&#xff0c;冬天的棉袄&#xff0c…

【HTML】入门教程

HTML入门 HTML网页主流浏览器及其内核W3C标准HTML结构简单示例meta HTML标签语法规则标签分类标签之间的关系标签属性通用属性&#xff1a; 常用标签及其属性应用示例 HTML5语义化标签多媒体标签视频标签 video音频标签 audio 新增的表单元素 参考文档 HTML HTML 指的是超文本…

使用llama.cpp 在推理MiniCPM-1.2B模型

llama.cpp 是一个开源项目&#xff0c;它允许用户在C中实现与LLaMA&#xff08;Large Language Model Meta AI&#xff09;模型的交互。LLaMA模型是由Meta Platforms开发的一种大型语言模型&#xff0c;虽然llama.cpp本身并不包含LLaMA模型的训练代码或模型权重&#xff0c;但它…