首页 > 其他分享 >GIL全局解释锁

GIL全局解释锁

时间:2024-04-09 20:47:26浏览次数:20  
标签:解释 多线程 线程 IO 进程 密集型 全局 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.)

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

  • 普遍使用的都是Cpython解释器

一、GIL锁与普通互斥锁的区别

  • 当子线程中过程中遇到IO阻塞时,GIL锁会放开并切换另一个至线程,因为线程之间的切换是非常快的,即使只是在我们看来只是阻塞了0.1秒但是已经有很多线程已经开始了,就导致许多线程在同一时间对一个数据进行修改,就出现了与预期不符的结果
from threading import Thread, Lock
import time

money = 100


def run():
    global money
    temp = money
    time.sleep(0.1)
    money = temp - 1


def main():
    t_list = []
    for i in range(50):
        t = Thread(target=run)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()
    print(money)


if __name__ == '__main__':
    # 50 个子线程同时对数据进行修改,都返回99
    main()  # 99

  • 当没有阻塞时,就会默认上GIL锁,同一时刻只允许一个线程开始,谁先开始谁先处理数据
from threading import Thread, Lock
import time

money = 100


def run():
    global money
    temp = money
    money = temp - 1


def main():
    t_list = []
    for i in range(50):
        t = Thread(target=run)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()
    print(money)


if __name__ == '__main__':
    main()  # 50
  • 自动加锁并解锁
  • 子线程启动 , 后先去抢 GIL 锁 , 进入 IO 自动释放 GIL 锁 , 但是自己加的锁还没解开 ,其他线程资源能抢到 GIL 锁,但是抢不到互斥锁
  • 最终 GIL 回到 互斥锁的那个进程上,处理数据
from threading import Thread, Lock
import time

money = 100
lock = Lock()


def run():
    lock.acquire()
    global money
    time.sleep(0.1)
    temp = money
    money = temp - 1
    lock.release()


def main():
    t_list = []
    for i in range(50):
        t = Thread(target=run)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()
    print(money)


if __name__ == '__main__':
    main()  # 50

二、GIL导致多线程无法利用多核优势

[1] Cpython 解释器中 GIL

  • Cpython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时进行
    • 同一个进程下的多个线程无法利用多线程并发这一优势吗?
    • Python的多线程是不是一点用都没有?
  • 因为在Cpython中的内存管理不是线程安全的
    • PS:内存管理(垃圾回收机制)
      • 应用计数
      • 标记清除
      • 分代回收

[2]Python的多线程是不是一点用都没有?

  • 同一个进程下的多线程无法利用多核优势,是不是就没用了
  • 多线程是否有用要看情况
    • 单核
      • 四个任务(IO密集型/计算密集型)
    • 多核
      • 四个任务(IO密集型/计算密集型)

(1)计算密集型

一直处在计算运行中

  • 每个任务都需要 10s
    • 单核
      • 多进程:额外消耗资源
      • 多线程:减少开销
    • 多核
      • 多进程:总耗时 10s
      • 多线程:总耗时 40s+

(2)IO密集型

存在多个 IO 阻塞切换操作

  • 每个任务都需要 10s
    • 多核
      • 多进程:相对浪费资源
      • 多线程:更加节省资源

[3]小结

  • 计算是消耗CPU的:
    • 代码执行,算术,for都是计算
  • IO不消耗CPU:
    • 打开文件,写入文件,网络操作都是IO
    • 如果遇到IO,该线程会释放CPU的执行权限,CPU转而去执行别的线程
  • 由于python有GIL锁,开启多条线程,统一时刻,只能有一条线程在执行
  • 如果是计算密集型,开了多线程,同一时刻,只有一个线程在执行
  • 多核CPU,就会浪费多核优势
  • 如果是计算密集型,我们希望,多个核(CPU),都干活,同一个进程下绕不过gil锁
  • 所以我们开启多进程,GIL锁只能锁住某个进程中得线程,开启多个进程,就能利用多核优势
  • IO密集型---》只要遇到IO,就会释放CPU执行权限
  • 进程内开了多个IO线程,线程多半都在等待,开启多进程是不能提高效率的,反而开启进程很耗费资源,所以使用多线程即可

(1)计算密集型任务(多进程)

  • 计算密集型任务主要是指需要大量的CPU计算资源的任务,其中包括执行代码、进行算术运算、循环等。
    • 在这种情况下,使用多线程并没有太大的优势。
    • 由于Python具有全局解释器锁(Global Interpreter Lock,GIL),在同一时刻只能有一条线程执行代码,这意味着在多线程的情况下,同一时刻只有一个线程在执行计算密集型任务。
  • 但是,如果使用多进程,则可以充分利用多核CPU的优势。
    • 每个进程都有自己独立的GIL锁,因此多个进程可以同时执行计算密集型任务,充分发挥多核CPU的能力。
    • 通过开启多个进程,我们可以将计算密集的任务分配给每个进程,让每个进程都独自执行任务,从而提高整体的计算效率。

(2)IO密集型任务(多线程)

  • IO密集型任务主要是指涉及大量输入输出操作(如打开文件、写入文件、网络操作等)的任务。
    • 在这种情况下,线程往往会因为等待IO操作而释放CPU执行权限,不会造成太多的CPU资源浪费。
    • 因此,使用多线程能够更好地处理IO密集型任务,避免了频繁切换进程的开销。
  • 当我们在一个进程中开启多个IO密集型线程时,大部分线程都处于等待状态,开启多个进程却不能提高效率,反而会消耗更多的系统资源。
    • 因此,在IO密集型任务中,使用多线程即可满足需求,无需开启多个进程。

(3)总结

  • 计算密集型任务使用多进程可以充分利用多核CPU的优势,而IO密集型任务使用多线程能够更好地处理IO操作,避免频繁的进程切换开销。
    • 根据任务的特性选择合适的并发方式可以有效提高任务的执行效率。

三、GIL特点总结

  • 1.GIL 不是python的特点而是Cpython解释器的特点
  • 2.GIL 保证解释器级别的数据的安全
  • 3.GIL会导致同一个进程下的多个线程的无法同时进行即无法利用多核优势
  • 4.针对不同的数据还是需要加不同的锁处理
  • 5.解释型语言的通病:同一个进程下的多个线程无法利用多核优势

标签:解释,多线程,线程,IO,进程,密集型,全局,GIL
From: https://www.cnblogs.com/taoyuanshi/p/18124726

相关文章

  • 个人电子实验室--神器 Digilent AD2/3
    AD2简介Digilent的AnalogDiscovery2(AD2)是一款功能强大的USB示波器和多功能仪器,对于个人电子实验室来说,它提供了许多有用的功能,可以大大扩展你的实验和学习范围。这些特性使得AD2成为一个理想的选择,无论是工程学生、业余爱好者还是电子发烧友,都可以利用它在几乎任何地方进......
  • 处理全局样式
    示例本次采用vite+vue3首先在@/themes/variable.scss文件创建:root{ --bg-color:#22416f;//默认背景颜色--color:rgb(39,204,207);--el-color-primary:#fff;}$bg-color:var(--bg-color);//默认背景颜色$color:var(--color);$All-color:var(--el-col......
  • MySQL全局锁,表锁,行锁
    数据库锁设计的初衷是处理并发问题,作为多用户共享的资源,当出现并发访问的时候,数据库需要合理的控制资源的访问规则,而锁就是用来实现这些访问规则的重要数据结构根据加锁的范围,MySQL里的锁大概可以分为全局锁,表级锁,行锁三类一、全局锁全局锁就是对整个数据库实例加锁,MySQL提供......
  • 欧盟网络安全局:公共数据空间中的个人数据保护设计(上)
    文章目录前言一、背景:欧盟数据空间(一)欧盟数据空间的设计原则二、欧盟数据空间中数据保护的注意事项(一)欧盟数据空间关键术语(二)数据共享环境中输入隐私和输出隐私问题(三)数据保护工程的作用(四)数据空间中的数据保护影响评估(五)问责制的主要内容(六)通过保障措施......
  • windows消息机制--1基本概念解释
    基本概念解释我们在编写标准C程序的时候,经常会调用各种库函数来辅助完成某些功能:初学者使用得最多的C库函数就是printf了,这些库函数是由你所使用的编译器厂商提供的。在Windows平台下,也有类似的函数可供调用:不同的是,这些函数是由Windows操作系统本身提供的。1)SDK和APISD......
  • 解释一下 "*.ts?(x)": [ "prettier --no-error-on-unmatched-pattern --cache --parse
    这段配置来自于一个项目的构建工具(如ESLint、Gulp、Webpack等)或者是一个任务运行器(如npmscripts、Makefile、gulpfile.js等)中的脚本命令,它通常是在lint-staged、husky等预提交钩子(GitHooks)配置中用来指定对特定类型文件进行格式化的指令。具体来说:"*.ts?(x)":这是一个glob......
  • axios配置全局过滤器
    importaxiosfrom'axios'constservice=axios.create({baseURL:'/api',//注意!!这里是全局统一加上了'/api'前缀,也就是说所有接口都会加上'/api'前缀在,页面里面写接口的时候就不要加'/api'了,否则会出现2个'/api',类似'/api/api/user'......
  • vue 全局组件 局部组件
    全局组件:<script>//创建vue实例constapp=Vue.createApp({template:`<div><hello/><world/><hello-world/></div>`});//子组件//组件具备复用性//全局组件,只要定义了,处处可以使用,性能不高,但是使用起来简单......
  • Vue3 · 小白学习全局 API:常规
    全局API:常规本次笔记versionnextTick()defineComponent()defineAsyncComponent()defineCustomElement()1.version暴露当前所使用的Vue版本。类型string示例import{version}from'vue'console.log(version)2.nextTick()等待下一次DOM更新刷新的工具......
  • C++之静态变量和全局变量的区别
    全局变量和静态变量的存储方式是一样的,只是作用域不同。静态局部变量具有局部作用域只对定义自己的函数可见,只被初始化一次,自从初始化一次之后直到程序运行期间一直都在。静态全局变量具有全局作用域作用于定义它的程序文件但是不能作用于项目里的其它文件,这一点和全局变......