对 Python 中 GIL 的理解

一.Python 中的 GIL

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

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

1.GIL 存在原因

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

2.GIL 的影响

(1)多线程并发受限

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

(2)I/O 密集型任务

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

3.如何绕过 GIL

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

二.GIL 与多进程

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

1.GIL 与多进程关系

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

2.多进程的实现

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

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 与多线程

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

1.GIL 与多线程的限制

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

(1)CPU 密集型任务

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

(2)频繁切换 GIL

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

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

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

3.GIL 下有效使用多线程

(1)I/O 密集型任务

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

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 核心上,从而真正实现并行计算。

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 的影响。

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.xdnf.cn/news/1541681.html

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

相关文章

低代码可视化工具-uniapp页面跳转传参-代码生成器

uniapp页面跳转传参 在uni-app中,页面间的跳转和传参是一个常见的需求。uni-app提供了多种页面跳转方式,如uni.navigateTo、uni.redirectTo、uni.reLaunch、uni.switchTab、uni.navigateBack等,每种方式适用于不同的场景。以 页面跳转并传参…

win7自带壁纸丢失主题丢失

有时候盗版破解或者其他美化工具会导致win7自带的壁纸丢失,从个性化管理里面无法恢复原始的壁纸(如下图),但是由于工作原因公司的电脑又不方便设置第三方的壁纸,所以找了一下解决方案。 经典问题,百度找到的…

软考中级软设背诵内容

冯诺依曼结构、哈佛结构 冯诺依曼结构: 程序指令和数据都采用二进制表示 程序指令和数据在同一个存储器中混合 程序的功能都由中央处理器(CPU)执行指令来实现 程序的执行工作由指令进行自动控制 SRAM、DRAM 与DRAM相比,SRAM集成率低、功…

详细剖析RocketMQ消息消费原理

本文参考转载至《RocketMQ技术内幕 第2版》 一. 消息消费概述 消息消费以组的模式开展,一个消费组可以包含多个消费者,每个消费组可以订阅多个主题,消费组之间有集群模式和广播模式两种消费模式。集群模式是当前主题下的同一条消息只允许被其…

hutool 解压缩读取源文件和压缩文件大小失败导致报错

前言 最近处理老项目中的问题,升级安全jar,发现hutool的jar在解压缩的时候报错了,实际上是很简单的防御zip炸弹攻击的手段,但是却因为hutool的工具包取文件大小有bug,造成了解压缩不能用,报错:…

2024年华为杯数学建模研赛(F题) 建模解析| 卫星轨道 | 小鹿学长带队指引全代码文章与思路

我是鹿鹿学长,就读于上海交通大学,截至目前已经帮2000人完成了建模与思路的构建的处理了~ 本篇文章是鹿鹿学长经过深度思考,独辟蹊径,实现综合建模。独创复杂系统视角,帮助你解决研赛的难关呀。 完整内容可…

代码随想录Day 51|题目:99.岛屿数量、100.岛屿的最大面积

提示:DDU,供自己复习使用。欢迎大家前来讨论~ 文章目录 题目一:99. 岛屿数量思路深度优先搜索DFS广度优先搜索BFS 题目二:100. 岛屿的最大面积DFSBFS 总结 题目一:99. 岛屿数量 99. 岛屿数量 (kamacoder.com) 思路 …

Java高级Day48-JDBC-API和JDBC-Utils

127.JDBC API 128.JDBC-Utils public class JDBCUtils {//这是一个工具类,完成mysql的连接和关闭资源//顶柜相关的属性(4个),因为只需要一份,因此做成staticprivate static String user;//用户名private static Stri…

Vision Transformer (ViT)、Swin Transformer 和 Focal Transformer

1. Vision Transformer (ViT) Vision Transformer详解-CSDN博客https://blog.csdn.net/qq_37541097/article/details/118242600?ops_request_misc%257B%2522request%255Fid%2522%253A%2522F8BBAFBF-A4A1-4D38-9C0F-9A43B56AF6DB%2522%252C%2522scm%2522%253A%252220140713.13…

如何把python(.py或.ipynb)文件打包成可运行的.exe文件?

将 Python 程序打包成可执行的 .exe 文件,通常使用工具如 PyInstaller。这是一个常用的 Python 打包工具,可以将 Python 程序打包成独立的可执行文件,即使没有安装 Python 也能运行。 步骤: 1. 安装 PyInstaller 使用 conda 安…

如何在Linux Centos7系统中挂载群晖共享文件夹

前景:企业信息化各种系统需要上传很多的图片或者是文件,文件如何在群晖中显示,当文件或者图片上传到linux指定文件夹内,而文件夹又与群晖共享文件夹进行挂载,就能保证上传的文件或者图片出现在群晖并在群晖里进行管理。…

Java之继承1

1. 继承 1.1 为什么要继承 在Java中我们定义猫类和狗类,如下 public class Cat {public String name;public int age;public String color;public void eat(){System.out.println(name "正在吃饭");}public void sleep(){System.out.println(name &qu…

网页聊天——测试报告——Selenium自动化测试

一,项目概括 1.1 项目名称 网页聊天 1.2 测试时间 2024.9 1.3 编写目的 对编写的网页聊天项目进行软件测试活动,揭示潜在问题,总结测试经验 二,测试计划 2.1 测试环境与配置 服务器:云服务器 ubuntu_22 PC机&am…

国庆电影扎堆来袭,AI智能体帮你推荐必看佳片!(附制作教程)

大家好,我是凡人。 今天看到新闻,发现国庆有10部影片要扎堆儿上映,对于选择困难症的我属实有点难选,同时也想避开一些坑省的浪费金钱和时间。 本着不知道就问AI的习惯,想问问大模型怎么看,但做了简单的交…

Go语言基础学习02-命令源码文件;库源码文件;类型推断;变量重声明

命令源码文件 GOPATH指向的一个或者多个工作区,每个工作区都会有以代码包为基本组织形式的源码文件。 Go语言中源码文件可以分为三类:命令源码文件、库源码文件、测试源码文件。 命令源码文件: 命令源码文件是程序的运行入口,是每…

descrTable常用方法

descrTable 为 R 包 compareGroups 的重要函数,有关该函数以及 compareGroups 包的详细内容见:R包compareGroups详细用法 加载包和数据 library(compareGroups)# 加载 REGICOR 数据(横断面,从不同年份纳入,每个变量有…

十五、差分输入运算放大电路

差分输入运算放大电路 1、差分输入运算放大电路的特点、用途, 2、输出信号电压与输入信号电压的关系。

Python | Leetcode Python题解之第420题强密码检验器

题目: 题解: class Solution:def strongPasswordChecker(self, password: str) -> int:n len(password)has_lower has_upper has_digit Falsefor ch in password:if ch.islower():has_lower Trueelif ch.isupper():has_upper Trueelif ch.isdi…

YOLOv10 简介

YOLOv10,由清华大学的研究人员基于 Ultralytics Python 包构建,引入了一种全新的实时目标检测方法,该方法解决了以往 YOLO 版本中后处理和模型架构方面的不足。通过消除非极大值抑制(NMS)并优化各种模型组件&#xff0…

Linux文件IO(五)-三种进程退出方法及空洞文件

1.三种进程退出方法 return 当程序在执行某个函数出错的时候,如果此函数执行失败会导致后面的步骤不能在进行下去时,应该在出错时终止程序运行,不应该让程序继续运行下去,那么如何退出程序、终止程序运行呢?有过编程…