GIL全局解释器锁

news2024/12/23 6:08:03

文章目录

  • GIL全局解释器锁
    • 一、引入:
    • 二、GIL介绍
    • 三、GIL与Lock
    • 四、GIL与多线程
    • 总结

GIL全局解释器锁

一、引入:

首先要明白,GIL并不是Python的一个特性,其实在我们通常所称呼的Python解释器,其实是CPython解释器,因为大部分Python程序都是基于该解释器执行的,当然还有JPython解释器(基于Java编写的),而这个GIL则是CPython解释器的特性,而不是Python的特性。

GIL全称:Global Interpreter Lock,官方解释

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

结论:在CPython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

二、GIL介绍

GIL本质上也是一把互斥锁,既然是互斥锁,本质上都是将并发变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

GIL达到的效果则是,同一时间内只有一个线程能够拿到GIL锁,拿到GIL锁后,该线程就可以使用解释器进行操作。

用一个小例子说明GIL的小部分作用:

没有GIL锁的话,如果有两个线程,一个线程是给变量添加数据,另一个线程则是垃圾回收机制的线程,此时可能就会出现一个问题,一个线程在定义数据产生后,还没有来的及将该数据的内存地址绑定给变量,就被垃圾回收机制给回收了(因为检测到这个数据计数为0),由于都是同一进程下的线程,且还是并发执行的,如果真的是这样执行,将给我们的程序造成很大的隐患。

三、GIL与Lock

可以存在的疑惑,既然有了GIL锁来保证同一时间只能有一个线程运行,那还需要Lock锁干嘛?

首先,我们需要达成共识:锁的目的是为了保护共享的数据,同一时间只能有一个线程来修改共享的数据

然后,我们可以得出结论:保护不同的数据就应该加不同的锁。

最后,问题就很明朗了,GIL 与Lock是两把锁,保护的数据不一样,前者是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据),后者是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自定义加锁处理,即Lock

GIL保护的是解释器级别的数据,保护用户自己的数据则需要自己加锁处理,如下图:

在这里插入图片描述
再来分析我们的疑问:

  1. 如果现在存在100个线程,那么其中某一个线程会抢到GIL锁,我们且称它为:线程1。
  2. 当线程1拿到GIL可以使用解释器后,线程1使用一个函数对全局变量count进行+1操作之前进行了互斥锁Lock。
  3. 而此时线程1还未执行完+1操作,线程2就把GIL锁给抢走了,此时线程2拿到这个GIL也想来执行这个+1操作,但执行这个函数时,发现有一个Lock未被释放,那它只能阻塞住,被释放GIL。
  4. 当线程1再次抢到这个GIL后,继续在上一次暂停的位置完成的+1操作,做完以后,再把Lock、GIL给释放掉。此后其它线程重复1、2、3、4步骤

保护不同的数据就需要加不同的锁

四、GIL与多线程

有了GIL的存在,同一时刻同一进程中只有一个线程被执行

创建进程开销大、而线程开销小,却无法利用多核优势,Python不行了?
要解决这个问题,我们需要在几个点上达成一致:

  1. cpu到底是用来做计算的,还是用来做I/O的?
  2. 多cpu,意味着可以有多个核并行完成计算,所以多核提升的是计算性能
  3. 每个cpu一旦遇到I/O阻塞,仍然需要等待,所以多核对I/O操作没什么用处

所以我们Python中,多线程是并发操作的,而多进程是可以做到并行操作的。

那么在Python里遇到I/O操作时,线程所用资源以及速度都会优于进程的。

而如果计算的话,多进程则更占据优势

代码示例:计算密集型,多进程效率更高

from multiprocessing import Process
from threading import Thread
import os, time


def work():
    res = 0
    for i in range(100000000):
        res *= i

if __name__ == '__main__':
    l = []
    print(os.cpu_count())  # 本机为8核
    start = time.time()
    for i in range(4):
        p = Process(target=work)  # 多进程、耗时6s多
        # p = Thread(target=work)  # 多线程、耗时19s多
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop = time.time()
    print('run time is %s' % (stop - start))

代码示例:I/O密集型,模拟I/O操作等待时间,线程并发效率更高

from multiprocessing import Process
from threading import Thread
import os, time


def work():
    time.sleep(4)


if __name__ == '__main__':
    l = []
    print(os.cpu_count())  # 本机为8核
    start = time.time()
    for i in range(4):
        # p = Process(target=work)  # 多进程、耗时4.1s左右
        p = Thread(target=work)  # 多线程、耗时4.005s左右
        l.append(p)
        p.start()
    for p in l:
        p.join()
    stop = time.time()
    print('run time is %s' % (stop - start))

因为开启进程的开销会远大于开启线程,而做这种I/O较多的操作,多线程会更具有优势

应用场景举例:

多线程用于IO密集型,如socket,爬虫,web
多进程用于计算密集型,如金融分析

总结

  1. 因为GIL的存在,只有IO较多场景下的多线程会得到较好的性能。
  2. 如果对并行计算性能较高的程序可以考虑把核心部分也成C模块,或者使用多进程实现。
  3. GIL在较长一段时间内将会继续存在,但是会不断对其进行改进。

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

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

相关文章

excel, 熟悉了一般应用的函数,为何还有必要学vba

背景: 1. win10, office 2016, hp Elitebook笔记本电脑 2. 统计数量时,在sheet (工作表)页面使用函数 sum通过类似 sum(c6:c8) 进行求和,其中C7未被计算在内,例子见图片或附件。即使使用 “数据透视表”…

基于QT的学生考勤系统

低价有偿-获取代码: 知识付费时代,低价有偿获取代码,请理解! (1) 下载链接: 后补。 (2) 添加博主微信获取(有偿),备注来源: mryang511688 项目描述 技术:C、QT等 摘要: 信息技术的迅…

【Andriod】Appium的不同版本(Appium GUI、Appium Desktop、Appium Server )的安装教程

文章目录 前言一.Appium GUI二.Appium Desktop三.Appium Server 命令行版本1.安装node.js2.安装Appium Server 前言 Appium 安装提供两2方式:桌面版和命令行版。其中桌面版又分为 Appium GUI 和 Appium Desktop。 建议:使用Appium Desktop 一.Appium …

ubuntu安装Anaconda

下载 Anaconda 进入 Ubuntu,自己新建下载路径,输入以下命令开始下载 注意,如果不是 x86_64,需要去镜像看对应的版本(https://mirrors.bfsu.edu.cn/anaconda/archive/?CM&OA) wget https://mirrors.…

【Java 进阶篇】深入了解 Bootstrap 表格和菜单

表格和菜单是网页设计中的重要组成部分,它们用于展示数据、导航和用户交互。Bootstrap 是一个强大的前端框架,提供了丰富的表格样式和菜单组件,使开发者能够轻松创建功能丰富的网页。在本文中,我们将深入探讨 Bootstrap 中表格和菜…

火热的“出海潮”背后,你还应该知道些什么?

点击文末“阅读原文”即可报名参会 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 封面 / 姝琦Midjourney 产品统筹 / bobo 场地支持 / 声湃轩北京录音间 联合制作 / RTE开发者社区 过去几十年,入华的科技企业“走进来”,加速了国内企业的数…

VM虚拟机创建centos7 64位系统提示此主机不支持64位客户机操作系统,此系统无法运行

VM虚拟机创建centos7 64位系统提示此主机不支持64位客户机操作系统,此系统无法运行 背景解决方案 背景 本身系统是window10 64位专业版系统,理论上不应该不支持64位的。 解决方案 最近安装docker开启了虚拟化hyper-v,关闭即可。 打开cmd(…

Vue中的v-if和v-show指令有什么区别?

在Vue中,v-if和v-show是两个常用的指令,用于根据条件控制元素的显示和隐藏。虽然它们都可以根据条件来切换元素的可见性,但它们的实现和行为有一些区别。 1:编译时机: v-if是在编译阶段进行条件判断,如果条件为false,则在DOM中不会渲染该元素及其子元素;v-show则是在运…

MongoDB深度学习

MongoDB的简介 什么是MongoDB? MongoDB是一个基于分布式文件存储的数据库,由C语言编写。MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的,它支持的数据结构非常松散&…

解码eBPF可观测性:eBPF如何改变我们所知的观测性

让我们来看看eBPF——这项技术到底是什么,它如何影响观测性,它与现有的观测性实践有什么区别,未来可能会发生什么变化? 在过去的两年里,云原生社区一直在热烈讨论eBPF。eBPF曾是KubeCon、eBPF Days和eBPF Summit的主题…

SystemVerilog Assertions应用指南 Chapter 1.17使用参数的SVA检验器

1.17使用参数的SVA检验器 SVA允许像 Verilog那样在检验器中使用参数( parameter),这为创建可重用的属性提供了很大的灵活性。比如,两个信号间的延迟信息可以在检验器中用参数表示,那么这种检验器就可以在设计只有时序关系不同的情况中重用。例子1.2显示了个带延迟默…

【C++】一些C++11特性

C特性 1. 列表初始化1.1 {}初始化1.2 initializer_list 2. 声明2.1 auto2.2 typeid2.3 decltype2.4 nullptr 3. STL3.1 新容器3.2 新接口 4. 右值引用5. 移动构造与移动赋值6. lambda表达式7. 可变参数模板8. 包装器9. bind 1. 列表初始化 1.1 {}初始化 C11支持所有内置类型和…

Spring中自定义类型转换器

目录 一、什么是类型转换器 二、自定义类型转化器 2.1 实现Converter接口 2.2 在Spring中注册 三、自定义类型转换器中的细节 3.1 解决代码的耦合 3.2 注册标签id值必须唯一 ​3.3 Spring提供的日期转换器 一、什么是类型转换器 在Spring中类型转换器就是将配置文件中的字符串…

力扣一.链表的运用

一.合并生序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 输入:l1 = [], l2 = [] 输出:[] 输入:l1 = [], l2 = [0] 输出:[0]提示: 两个链表的…

【【萌新的FPGA学习之管脚设定xdc文件】】

萌新的FPGA学习之管脚设定xdc文件 xdc文件可以自己设置 也可以匹配 我们根据正点原子的流水灯管脚设定 主要讲述一下 各个英文设计是什么意思 Name:工程中顶层端口的名称。 Direction:说明管脚是输入还是输出。 Neg Diff Pair:负差分对&…

YOLOv5/v7/v8改进实验(三)之训练技巧篇

🚀🚀 前言 🚀🚀 YOLO系列是一种目标检测算法,具有高效的实时性能和较高的准确性。一些常用的YOLO训练技巧往往可以帮助提高模型的性能和收敛速度。而这些技巧在YOLOv5、YOLOv7和YOLOv8几乎通用。 🔥&#…

CUDA编程入门系列(十一)CUDA程序优化技巧

优化原则:最大化并行执行 探索并行化: 优化线程块的规模 我们在设计CUDA程序的时候,要对线程块的个数进行考虑。因为GPU中流处理器SM的数量是相对固定的,所以我们应该尽量的将多个block放到同一个SM当中(至少保证每个…

三、信号与槽

1. 信号槽的定义 信号函数和槽函数是Qt在C的基础上新增的功能,功能是实现对象之间的通信。 实现信号槽需要有两个先决条件: 通信的对象必须是从QObject派生出来的 QObject是Qt所有类的基类。 类中要有Q_OBJECT宏 2. 信号槽的使用 2.1 函数原型 最常…

免费SSL证书:JoySSL让您的网站更安全

在今天的数字化时代,保护网站和用户信息的安全至关重要。SSL(Secure Sockets Layer)证书通过加密网站与用户之间的通信,确保数据传输的安全性。让您拥有一个SSL加密的网站是至关重要的,但您可能会担心高昂的费用。不过…

秦丝科技“羽”深大计软学院同行,共庆深圳大学成立40周年

2023年10月21日, 由深圳大学工会主办 , 深圳大学体育学院 、深圳大学后勤部、丽湖校区管委会等单位协办的深圳大学2023年教职工羽毛球联赛-第一场正式举办; 由深圳市秦丝科技有限公司联合头部商户共同赞助的计算机与软件学院取得首场胜利&…