• python面试题——什么是GIL ;什么时候释放GIL锁;互斥锁(同步锁)和GIL的区别


    在这里插入图片描述

    一、什么是GIL

    GIL 是python的全局解释器锁,同一进程中假如有多个线程运行,一个线程在运行python程序的时候会霸占python解释器(加了一把锁即GIL),使该进程内的其他线程无法运行,等该线程运行完后其他线程才能运行。如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。所以在多线程中,线程的运行仍是有先后顺序的,并不是同时进行。

    我们可以把GIL看作是“通行证”,并且在一个python进程中,GIL只有一个。拿不到通行证的线程,就不允许进入CPU执行。GIL只在cpython中才有。

    二、什么时候释放GIL锁

    1、某个线程运行完后其他线程才能运行。
    2、如果线程运行过程中遇到耗时操作,则解释器锁解开,使其他线程运行。

    三、互斥锁(同步锁)和GIL的区别

    1、举例:多线程执行任务造成数据混乱的问题

    import threading
    
    g_num=0
    def run():
        global g_num
    
        for i in range(1000000):
            g_num+=1
        print(f'当前线程{threading.current_thread().name}的执行结果:',g_num)
    
    
    if __name__ == '__main__':
        thread=[]
        for i in range(10):
            t=threading.Thread(target=run)
            t.start()
            thread.append(t)
    
        for t in thread:
            t.join()
        print('主线程执行完毕')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    执行结果:很明显数据已经混乱了

    当前线程Thread-1的执行结果: 1000000
    当前线程Thread-2的执行结果:当前线程Thread-3的执行结果: 1581377
     当前线程Thread-4的执行结果:2690126
     当前线程Thread-5的执行结果: 35138763690126
    
    当前线程Thread-6的执行结果: 当前线程Thread-7的执行结果: 5545936
    4690126
    当前线程Thread-8的执行结果:当前线程Thread-9的执行结果:  6244936
    当前线程Thread-10的执行结果: 82094017209401
    
    主线程执行完毕
    
    Process finished with exit code 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2、数据混乱的原因:

    cpu分成多个时间片段,启动10线程,分配10个cpu时间片段,当我累加数字设置比较小的时候,在单个cpu时间片段内,for循环代码就执行完,就不会产生数据混乱的。当我数据设置的比较大时,在单个cpu时间片段内,for循环代码就执行不完,并且没有分配2个或2个以上的连续的cpu时间片段,导致一个cpu时间片段没有执行完该线程,下一个线程开始执行了

    问题产生的原因就是没有控制多个线程对同一资源的访问,对数据造成破坏,使得线程运行的结果不可预期。这种现象称为"线程不安全”"。

    3、解决数据混乱(同步锁、互斥锁)

    lock=Lock():创建同步锁
    lock.acquire():获得这把锁的钥匙
    lock.release():释放锁;如果不释放锁,其他的线程拿不到锁,是不会运行的;
    释放锁后其他线程会抢这把锁。谁先抢到这把锁不一定
    必须使用同一把锁

    import threading
    
    
    g_num=0
    def run():
        global g_num
    
        lock.acquire()
        for i in range(1000000):
            g_num+=1
        print(f'当前线程{threading.current_thread().name}的执行结果:',g_num)
        lock.release()
    
    if __name__ == '__main__':
        lock=threading.Lock()
        thread=[]
        for i in range(10):
            t=threading.Thread(target=run)
            t.start()
            thread.append(t)
    
        for t in thread:
            t.join()
        print('主线程执行完毕')
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    执行结果:

    当前线程Thread-1的执行结果: 1000000
    当前线程Thread-2的执行结果: 2000000
    当前线程Thread-3的执行结果: 3000000
    当前线程Thread-4的执行结果: 4000000
    当前线程Thread-5的执行结果: 5000000
    当前线程Thread-6的执行结果: 6000000
    当前线程Thread-7的执行结果: 7000000
    当前线程Thread-8的执行结果: 8000000
    当前线程Thread-9的执行结果: 9000000
    当前线程Thread-10的执行结果: 10000000
    主线程执行完毕
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    四、总结:

    多线程编程时通过调用threading模块的Lock函数,来获取一把互斥锁。互斥锁就是对共享数据进行锁定,保证同一时刻只有一个线程操作数据,是数据级别的锁。

    GIL锁是解释器级别的锁,保证同一时刻进程中只有一个线程拿到GIL锁,拥有执行权限。
    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    使用 nvm 实现对 Node 的多版本管理
    在win10命令行(cmd)中添加临时环境变量
    模拟器-雷电-使用adb push或adb pull操作文件
    LeetCode_递归_中等_397.整数替换
    阿里云/腾讯云被攻击后怎么秒解黑洞
    STM8S系列基于STVD开发,自定义printf函数+TIM5精确延时函数模块化工程示例
    前端性能优化 —— 使用 BMP 图片代替 canvas.toDataURL
    Wireshark抓包分析TCP协议:三次握手和四次挥手
    你的关联申请已发起,请等待企业微信的管理员确认你的申请
    用于机器学习的 Pandas(ML)
  • 原文地址:https://blog.csdn.net/YZL40514131/article/details/126198377