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

GIL全局解释器锁

时间:2024-01-28 17:33:32浏览次数:23  
标签:解释器 多线程 list 线程 time 密集型 全局 GIL

GIL全局解释器锁

(1)简介

  • 在 CPython 中,GIL(全局解释器锁)是一种机制,用于确保在同一时刻只有一个线程执行 Python 字节码。这个锁对于 Python 解释器来说是必要的,因为 CPython 的内存管理并不是线程安全的。当多个线程试图执行 Python 代码时,GIL 会确保同一时刻只有一个线程能够执行字节码指令,这样可以避免多线程并发执行时的数据竞争和内存一致性问题。

  • 简单来说,GIL 会在解释器级别上进行加锁,这意味着在同一时刻只有一个线程能够执行 Python 代码。即使在多核处理器上,由于 GIL 的存在,Python 解释器仍然只能够利用单个核心执行 Python 代码,因为即使有多个线程,它们也无法同时执行 Python 字节码指令。

  • 虽然 GIL 确保了线程安全,但它也带来了一些缺点。其中之一是它限制了 Python 在多核处理器上的并行性能,因为无法充分利用多个 CPU 核心。这也意味着在 CPU 密集型的任务中,Python 程序的性能可能受到影响。然而,对于 I/O 密集型任务,GIL 的影响通常较小,因为线程在等待 I/O 操作完成时会释放 GIL,允许其他线程执行。

from threading import Thread,Lock
import time

money = 100

lock = Lock()

def task():
    global money
    with lock:
        temp = money
        time.sleep(2)
        money = temp - 1


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

    for t in t_list:
        t.join()

    # 所有子线程结束后打印 money
    print(money)


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

(2)GIL导致多线程无法利用多核优势

  • Cpython解释器中GIL是一把互斥锁,用来阻止同一个进程下的多个线程的同时进行

    • 同一个进程下的多个线程无法利用这一优势?

    • Python的多线程是不是一点用都没有?

  • Cpython中的内存管理不是线程安全的

    • ps:内存管理(垃圾回收机制)
      • 应用计数
      • 标记清除
      • 分代回收

(1)Python的多线程是不是一点用都没有?

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

(1)计算密集型

  • 每个任务都需要 10s
    • 单核
      • 多进程:额外消耗资源
      • 多线程:减少开销
    • 多核
      • 多进程:总耗时 10s+
      • 多线程:总耗时 40s+
from multiprocessing import Process
from threading import Thread
import time, os


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


def main_t():
    p_list = []
    # 获取当前CPU运行的个数
    print(os.cpu_count())
    start_time = time.time()
    for i in range(12):
        p = Process(target=work)
        p.start()
        p_list.append(p)

    for p in p_list:
        p.join()

    print(f'总耗时:>>>{time.time() - start_time}')


def main_p():
    t_list = []
    # 获取当前CPU运行的个数
    print(os.cpu_count())
    start_time = time.time()
    for i in range(12):
        t = Thread(target=work)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print(f'总耗时:>>>{time.time() - start_time}')


if __name__ == '__main__':
    main_t()  # 总耗时:>>>13.233189344406128
    main_p()  # 总耗时:>>>45.123342752456665

(2)IO密集型

  • 每个任务都需要 10s
    • 多核
      • 多进程:相对浪费资源
      • 多线程:更加节省资源
from multiprocessing import Process
from threading import Thread
import time, os


def work():
    time.sleep(2)


def main_t():
    p_list = []
    # 获取当前CPU运行的个数
    print(os.cpu_count())
    start_time = time.time()
    for i in range(400):
        p = Process(target=work)
        p.start()
        p_list.append(p)

    for p in p_list:
        p.join()

    print(f'总耗时:>>>{time.time() - start_time}')


def main_p():
    t_list = []
    # 获取当前CPU运行的个数
    print(os.cpu_count())
    start_time = time.time()
    for i in range(400):
        t = Thread(target=work)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    print(f'总耗时:>>>{time.time() - start_time}')


if __name__ == '__main__':
    main_t()  # 总耗时:>>>33.62358331680298
    main_p()  # 总耗时:>>>2.073925733566284

(3)总结

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

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

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

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

(3)总结

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

标签:解释器,多线程,list,线程,time,密集型,全局,GIL
From: https://www.cnblogs.com/ssrheart/p/17993057

相关文章

  • 编译器和解释器区别
    https://baijiahao.baidu.com/s?id=1772127690348492076&wfr=spider&for=pc1.1、编译器编译器(compiler)对于我们并不陌生,它主要为程序设计语言提供服务,它将各种各样的程序设计语言(比如:C、C++、RUST、JAVA等)进行处理,翻译成我们底层的计算机能够理解并执行相应动作。程序......
  • 解释器模式
    定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子简单来说:为了解释一种语言而为语言创建的解释器类型:行为型适用场景:某个特定类型问题发生频率足够高优点:语法由很多类表示,容易改变及扩展此“语言”缺点:当语法规则数目太多时,......
  • HTML中全局拦截回车键并将其转换为Tab键效果
    要在HTML中全局拦截回车键并将其转换为Tab键,可以使用JavaScript监听键盘事件,并在用户按下回车键时模拟Tab键的行为。以下是一个基本的实现方法:监听键盘事件:可以使用document.addEventListener来监听keydown事件。判断按键:在事件处理函数中,可以使用event.key来判断用户是否按下......
  • rust使用lazy_static对全局变量多线程并发读写示例
    首先需要在项目依赖Cargo.toml添加lazy_static依赖项[dependencies]lazy_static="1.4.0"示例代码如下:uselazy_static::lazy_static;usestd::sync::{RwLock,RwLockReadGuard,RwLockWriteGuard};usestd::thread;#[derive(Debug)]structSharedData{data:Vec<......
  • .NET集成IdGenerator生成分布式全局唯一ID
    前言生成分布式唯一ID的方式有很多种如常见的有UUID、Snowflake(雪花算法)、数据库自增ID、Redis等等,今天我们来讲讲.NET集成IdGenerator生成分布式全局唯一ID。分布式ID是什么?分布式ID是一种在分布式系统中生成唯一标识符的方法,用于解决多个节点之间标识符重复或性能问题。分布......
  • 二、nextjs API路由如何做好JWT登录鉴权、身份鉴权,joi字段校验,全局处理异常等(c-shoppi
    介绍在这篇文章中,我们将学习如何在C-Shopping电商开源项目中,基于Next.js14,处理所有API路由中添加身份验证和错误处理中间件的思路与实现。这篇文章中的代码片段取自我最近开源项目C-Shopping,完整的项目和文档可在https://github.com/huanghanzhilian/c-shopping地址查看。Next......
  • 二、nextjs API路由如何做好JWT登录鉴权、身份鉴权,joi字段校验,全局处理异常等(c-shoppi
    介绍在这篇文章中,我们将学习如何在C-Shopping电商开源项目中,基于Next.js14,处理所有API路由中添加身份验证和错误处理中间件的思路与实现。这篇文章中的代码片段取自我最近开源项目C-Shopping,完整的项目和文档可在https://github.com/huanghanzhilian/c-shopping地址查看。Next.js......
  • .NET 6 ASP.NET Core API 项目依赖注入一个全局对象,确保全局只实例化一次,调用的都是此
    在.NET6中,实现全局单例服务的方法是通过内置在ASP.NETCore中的依赖注入(DI)容器来完成的。DI容器负责创建和管理服务的实例,包括控制它们的生命周期。对于单例服务,DI容器将确保在应用程序的整个生命周期内只创建服务的一个实例,并且所有对该服务的请求都会返回这个单一的实例。以下......
  • 01_全局异常处理
    -**@RestControllerAdvice**定义全局异常处理类作用在所有的Controller类上-**@ExceptionHandler**声明处理异常的方法##实现步骤1.自定义异常```javapublicclassAccountNotFoundExceptionextendsException{publicAccountNotFoundException(){......
  • c#使用Hook钩子全局监听键盘和鼠标
    背景:今天接到客户一个需求,就是在收银员在用扫码枪扫顾客会员码或者微信付款码的时候判断用户有没有加企微好友和进企微群,然后根据这个状态进行语音播报,判断顾客能不能享受优惠价。关键难点就是用户用的收银系统是别家的,线上小程序用的是我们家的,两家不互通,所以立即决定采用Hook钩......