GIL全局解释器锁

【一】GIL全局解释器锁

明确一点:从多线程数据共享,数据可以被更改线程数次,可以看出存在GIL全局解释器锁

"""纯理论 不影响编程 只不过面试的时候可能会被问到"""
# 官方文档
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.)
"""
1.回顾
    python解释器的类别有很多
        Cpython Jpython Ppython
    垃圾回收机制
        应用计数、标记清除、分代回收
    
GIL只存在于CPython解释器中,不是python的特征
GIL是一把互斥锁用于阻止同一个进程下的多个线程同时执行
原因是因为CPython解释器中的垃圾回收机制不是线程安全的
​
反向验证GIL的存在 如果不存在会产生垃圾回收机制与正常线程之间数据错乱
GIL是加在CPython解释器上面的互斥锁
同一个进程下的多个线程要想执行必须先抢GIL锁 所以同一个进程下多个线程肯定不能同时运行 即无法利用多核优势
​
​
强调:同一个进程下的多个线程不能同时执行即不能利用多核优势
    很多不懂python的程序员会喷python是垃圾 速度太慢 有多核都不能用
​
反怼:虽然用一个进程下的多个线程不能利用多核优势 但是还可以开设多进程!!!
​
再次强调:python的多线程就是垃圾!!!
​
反怼:要结合实际情况 
    如果多个任务都是IO密集型的 那么多线程更有优势(消耗的资源更少)
        多道技术:切换+保存状态
    如果多个任务都是计算密集型 那么多线程确实没有优势 但是可以用多进程
        CPU越多越好
    
以后用python就可以多进程下面开设多线程从而达到效率最大化
"""
1.所有的解释型语言都无法做到同一个进程下多个线程利用多核优势
2.GIL在实际编程中其实不用考虑
 

【二】GIL与普通互斥锁区别

1.先验证GIL的存在
谁先抢到谁就先处理数据
from threading import Thread, Lock
import time
  money = 100
  def task():
      global money
      money -= 1#GIL加在这里
  for i in range(100):  # 创建一百个线程
      t = Thread(target=task)
      t.start()
  print(money)#0,由于GIL的存在,一次只能一个线程获得锁
2.当睡了 0.1s 后---》所有线程都拿到了money=100之后,才去都去抢那把 GIL 锁住的数据,当所有子线程都抢到后再去修改数据就变成了 99
from threading import Thread, Lock
money = 100
def task():
    global money
    temp = money
    time.sleep(0.1)
    money -= temp
def main():
    t_list = []
    for i in range(100):
        t = Thread(target=task)
        t.start()
        t_list.append(t)
​
    for t in t_list:
        t.join()
    print(money)
if __name__ == '__main__':
    main()
    # 99
3.要改变睡眠了之后被同时取数据,产生数据错乱
自动加锁并解锁
子线程启动 , 后先去抢 GIL 锁 , 进入 IO 自动释放 GIL 锁 , 但是自己加的锁还没解开 ,其他线程资源能抢到 GIL 锁,但是抢不到互斥锁
最终 GIL 回到 互斥锁的那个进程上,处理数据
from threading import Thread, Lock
import time
money = 100
mutex = Lock()
def task():
    global money
    mutex.acquire()
    tmp = money
    time.sleep(0.1)
    money = tmp - 1
    mutex.release()
    """
      抢锁放锁也有简便写法(with上下文管理)
      with mutex:
          pass
      """
    t_list = []
    for i in range(100):  # 创建一百个线程
        t = Thread(target=task)
        t.start()
        t_list.append(t)
        for t in t_list:
            t.join()
            # 为了确保结构正确 应该等待所有的线程运行完毕再打印money
            print(money)
【三】递归锁
可以被连续的 acquire 和 release
​
但是只能被第一个抢到这把锁上执行上述操作
​
他的内部有一个计数器,每acquire一次计数 +1 每release一次 计数-1
​
只要计数不为0,那么其他人都无法抢到该锁
from threading import Thread, Lock, RLock
import time
拿了就放
# 两个变量同时指向一把锁
metexA = metexB = RLock()
# 类只要加括号多次 产生的肯定不同的对象
# 如果你想要实现多次加括号等到的是相同的对象 - 单例模式
class MyThread(Thread):
    def run(self):
        self.func1()
        self.func2()
​
    def func1(self):
        metexA.acquire()
        # self.name:获取当前线程名
        print(f'{self.name} 抢到了A锁')
        metexB.acquire()
        print(f'{self.name} 抢到了B锁')
        metexB.release()
        metexA.release()
​
    def func2(self):
        metexB.acquire()
        # self.name:获取当前线程名
        print(f'{self.name} 抢到了A锁')
        time.sleep(2)
        metexA.acquire()
        print(f'{self.name} 抢到了B锁')
        metexA.release()
        metexB.release()
​
​
def main():
    for i in range(10):
        t = MyThread()
        t.start()
​
​
if __name__ == '__main__':
    main()
​
    # Thread-1 抢到了A锁
    # Thread-1 抢到了B锁
    # Thread-1 抢到了A锁
    # Thread-1 抢到了B锁
    # Thread-2 抢到了A锁
    # Thread-2 抢到了B锁
    # Thread-2 抢到了A锁
    # Thread-2 抢到了B锁
    # Thread-4 抢到了A锁
    # Thread-4 抢到了B锁
    # Thread-4 抢到了A锁
    # 不会卡主正常进行
【四】死锁
  • 死锁是指两个或多个进程,在执行过程中,因争夺资源而造成了互相等待的一种现象。
    
    from threading import Thread, Lock
    import time
    ​
    metexA = Lock()
    metexB = Lock()
    ​
    ​
    # 类只要加括号多次 产生的肯定不同的对象
    # 如果你想要实现多次加括号等到的是相同的对象 - 单例模式
    ​
    class MyThread(Thread):
        def run(self):
            self.func1()
            self.func2()
    ​
        def func1(self):
            metexA.acquire()
            # self.name:获取当前线程名
            print(f'{self.name} 抢到了A锁')
            metexB.acquire()
            print(f'{self.name} 抢到了B锁')
            metexB.release()
            metexA.release()
    ​
        def func2(self):
            metexB.acquire()
            # self.name:获取当前线程名
            print(f'{self.name} 抢到了A锁')
            time.sleep(2)#阻塞了线程2就开始了,拿了A,要B,但是B已经被1拿走了,1需要A,但是已经被2拿走了,两个线程僵持
            metexA.acquire()
            print(f'{self.name} 抢到了B锁')
            metexA.release()
            metexB.release()
    ​
    ​
    def main():
        for i in range(10):
            t = MyThread()
            t.start()
    ​
    ​
    if __name__ == '__main__':
        main()
        
        # Thread-1 抢到了A锁
        # Thread-1 抢到了B锁
        # Thread-1 抢到了A锁
        # Thread-2 抢到了A锁
        # 线程卡死
        # 开启十个线程 第一个线程走完第一圈 回到原地抢 A 结果第二个线程已经拿到了A 导致AB卡死

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【沐风老师】3DMAX样条线增强工具SplinePro使用方法详解

3DMAX样条线增强工具SplinePro使用教程 3DMAX样条线增强工具SplinePro,允许创建选定的多条样条曲线形状的轮廓并删除交叉点。 【适用版本】 3dMax2019 - 2025 【安装方法】 1.解压缩后,确认SplinePro-0.2.0.mse和logo.png两个文件在同一文件夹中。 2.…

python+django 环境搭建以及post接口封装

1、搭建pythondjango环境 python 3.7.9的版本 具体参考之前的安装教程 django 使用 pip install django 会自动安装 检验安装版本: python -m django --version 2、创建django项目 django-admin startproject projectname 启动项目:python manage.py…

verilog读写文件注意事项

文章目录 想要的16进制数是文本格式提供的文件,想将16进制数提取到变量内,想要的16进制数是文本格式提供的文件,想将16进制数提取到变量内,想要的16进制数是二进制格式提供的文件,想将16进制数提取到变量内&#xff0c…

大模型在营销领域的探索及创新

1 AIGA介绍 2 AIGA在营销领域的 应用和探索 3 总结与展望

【WPF】桌面程序开发之xaml页面基础布局方式详解

使用Visual Studio开发工具,我们可以编写在Windows系统上运行的桌面应用程序。其中,WPF(Windows Presentation Foundation)项目是一种常见的选择。然而,对于初学者来说,WPF项目中xaml页面的布局设计可能是一…

MySQL8.0在windows下的下载安装及详细使用

下载mysql8.0二进制包 下载地址:MySQL :: Download MySQL Community Server 编辑my.ini配置文件 解压二进制包,新建/编辑my.ini配置文件(如果不存在则新建) [client] #客户端设置,即客户端默认的连接参数 # 设置mysql客户端连接服务端时…

Python【打包exe文件两步到位】

Python打包Exe 安装 pyinstaller(pip install pyinstaller) 执行打包命令(pyinstaller demo.py) 打完包会生成 dist 文件夹,如下如

openrestry中的hello world

目录 概述实践部署openrestry脚本效果验证 概述 此篇将在 k8s 运行起一个 openrestry   环境:k8s:1.27.9 ,openrestry(docker镜像版本): 1.25.x ,k8s 与 ingress 请参考我的其它文章 离线镜像包请参考:op…

2024暑假集训

Day1——枚举 Day2——测试 Day3——贪心 Day4、5——测试 ——————————————————————————————————————————— Day3T7&Day5T7:没思路 Day3T8:不知道怎么排序筛选 Day5T5:没有算法难度,但是不知道怎么处理2队奶牛的情…

【TB作品】51单片机 Proteus仿真 超声波LCD1602ADC0832 身高体重测量仪

00024 超声波LCD1602ADC0832 实验报告:基于51单片机的身高体重测量仪设计 背景介绍 本实验设计并实现了一个基于51单片机的身高体重测量仪。该系统利用超声波传感器测量高度,通过ADC0832模数转换芯片获取重量数据,并使用LCD1602显示屏显示…

MySQL 中的 DDL、DML、DQL 和 DCL

文章目录 1. 数据定义语言(DDL)2. 数据操作语言(DML)3. 数据查询语言(DQL)4. 数据控制语言(DCL)总结 在 MySQL 数据库管理系统中,SQL 语句可以根据其功能分为不同的类别&…

电源纹波相关

什么是纹波?什么是噪声? 这种叠加在直流稳定量上的交流分量就称为纹波。 纹波的危害 电源纹波能影响设备性能和稳定性 纹波会导致电器上产生谐波,降低电源的使用效率; 高频电源纹波可能会产生浪涌电压或电流,影响设…

VSCode神仙插件——CodeSnap (好看的代码截图)

1 安装 2 使用 选中要截图的代码,右键 此时右侧会出现代码截图的预览图 如果要将截图保存到本地,则点击上图红色框中的图标 也可以点击下面截的图,CtrlC复制,然后就可以CtrlV粘贴到其他应用程序里了

拉曼光谱入门:3.拉曼光谱的特征参数与定量定性分析策略

1.特征参数 1.1 退偏振率 退偏振率(p)是一个衡量拉曼散射光偏振状态的参数,它描述了拉曼散射光的偏振方向与入射光偏振方向之间的关系。退偏振率定义为垂直偏振方向的拉曼散射强度与平行偏振方向的拉曼散射强度之比。退偏振率(p&…

shark云原生-日志体系-filebeat高级配置(适用于生产)-更新中

文章目录 1. filebeat.inputs 静态日志收集器2. filebeat.autodiscover 自动发现2.1. autodiscover 和 inputs2.2. 如何配置生效2.3. Providers 提供者2.4. Providers kubernetes2.5. 配置 templates2.5.1. kubernetes 自动发现事件中的变量字段2.5.2 配置 templates 2.6. 基于…

微信小程序简历Demo

微信小程序简历Demo 使用介绍最后获取源码 bilibili视频介绍 使用介绍 使用微信小程序实现的一个简历实现Demo 拖动马里奥,到指定Name下方 向上顶就可以显示对应的简历样式 点击头像可拨打电话 点击信息处可显示当前位置 最后 这是一个简单并且有趣的微信小程…

el-table 树形数据与懒加载 二级数据不展示

返回的数据中 children和hasChildren只能有一个,不能同时存在,否则加载数据会失败

端口被占用,使用小黑框查杀

netstat -ano (查看目前所有被占的端口) netstat -ano|findstr " 8080" 查一下目前被占用的端口号 ,目前我要查的端口号是:8080,注意 后面打8080的时候,要有空格,要不然报错 **task…

【React Native优质开源项目】

🌈个人主页: 程序员不想敲代码啊 🏆CSDN优质创作者,CSDN实力新星,CSDN博客专家 👍点赞⭐评论⭐收藏 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共…

17_VGG深度学习图像分类算法

1.1 简介 VGG网络,全称为Visual Geometry Group网络,是由牛津大学的Visual Geometry Group和谷歌DeepMind的研究人员共同提出的深度卷积神经网络模型。这一模型因在2014年ILSVRC(ImageNet大规模视觉识别挑战赛)中取得图像分类任务…