zoukankan      html  css  js  c++  java
  • (并发编程)全局解释器锁(GIL)-----有了GIL不用给线程加锁了?

    一、全局解释器锁 (GIL)
    运行test.py的流程:
    a、将python解释器的代码从硬盘读入内存
    b、将test.py的代码从硬盘读入内存  (一个进程内装有两份代码---一份cpython解释器代码一份test.py代码)
    c、将test.py中的代码像  字符串一样  读入python解释器中解析执行
     
    1 、GIL:全局解释器锁 (CPython解释器的特性)
    #本质就是一把互斥锁,相当于执行权限,每个进程内都会存在一把GIL
    #同一进程的多个线程必须抢到GIL才能使用CPython解释器解释执行自己的代码,
    #同一进程的多线程无法实现并行,但可以实现并发
    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(如果没有gil 多线程拿到解释器 并行(现在计算机都是多cup),当x=10的线程中内存中产生一个10,还没来的及绑定x,就有可能被垃圾回收机制回收,为了保证数据安全加gil锁).However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)
    GIL本质就是一把加在解释器身上的互斥锁(执行权限)。同一个进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码
     
    2、GIL的优缺点:
    优点:保证Cpython解释器内存管理的线程安全
    缺点:(一个进程内的线程只能一个一个并发执行)在Cpython解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,也就说Cpython解释器的多线程无法实现并行无法利用多核优势
    注意:
    a、GIL不能并行,但有可能并发,不一定为串行。(因为串行是一个任务完完全全执行完毕后才进行下一个;而cpython中,一个线程在io时,被CPU释放时,会被强行取消GIL的使用权限)
    b、多核(多CPU)的优势是提升运算效率
    c、计算密集型--》使用多进程,用上多核,同时存在(cup数的进程)数并行
      多进程:创建新进程(拷贝),切换进程---》开销大(时间上,内存上)
    d、IO密集型--》使用多线程 并发
      多线程:创建新线程,切换线程----》开销小(时间上,内存上)
     

    二、Cpython解释器并发效率验证
    1、计算密集型应该使用多进程
    from multiprocessing import Process
    from threading import Thread
    import time
    # import os
    # print(os.cpu_count())  #查看cpu个数
    def task1():
        res=0
        for i in range(1,100000000):
            res+=i
    def task2():
        res=0
        for i in range(1,100000000):
            res+=i
    def task3():
        res=0
        for i in range(1,100000000):
            res+=i
    def task4():
        res=0
        for i in range(1,100000000):
            res+=i
    if __name__ == '__main__':
        # p1=Process(target=task1)
        # p2=Process(target=task2)
        # p3=Process(target=task3)
        # p4=Process(target=task4)
        p1=Thread(target=task1)
        p2=Thread(target=task2)
        p3=Thread(target=task3)
        p4=Thread(target=task4)
        start_time=time.time()
        p1.start()
        p2.start()
        p3.start()
        p4.start()
        p1.join()
        p2.join()
        p3.join()
        p4.join()
        stop_time=time.time()
        print(stop_time - start_time)
    2、IO密集型应该使用多线程
    from multiprocessing import Process
    from threading import Thread
    import time
    def task1():
        time.sleep(3)
    def task2():
        time.sleep(3)
    def task3():
        time.sleep(3)
    def task4():
        time.sleep(3)
    if __name__ == '__main__':
        # p1=Process(target=task1)
        # p2=Process(target=task2)
        # p3=Process(target=task3)
        # p4=Process(target=task4)
        # p1=Thread(target=task1)
        # p2=Thread(target=task2)
        # p3=Thread(target=task3)
        # p4=Thread(target=task4)
        # start_time=time.time()
        # p1.start()
        # p2.start()
        # p3.start()
        # p4.start()
        # p1.join()
        # p2.join()
        # p3.join()
        # p4.join()
        # stop_time=time.time()
        # print(stop_time - start_time) #3.138049364089966
        p_l=[]
        start_time=time.time()
        for i in range(500):
            p=Thread(target=task1)
            p_l.append(p)
            p.start()
        for p in p_l:
            p.join()
    print(time.time() - start_time)
    三、线程互斥锁与GIL对比
    cpython中
    GIL能保护解释器级别代码(和垃圾回收机制有关)但保护不了其他共享数据(比如自己的代码)。所以在程序中对于需要保护的数据要自行加锁
    gil只是保证一个进程内的所有线程都是并发(不是串行吗)执行,[一个线程在io时,被CPU释放时,会被强行取消GIL的使用权限]
    主线程,线程1,线程2,垃圾回收线程》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》》
      线程1抢到gil 当拿到cpu 解释执行代码:抢到互斥锁 睡 (切—剥夺cpu-强行释放gil锁)改数据  放开互斥锁
      线程2抢到gil 当拿到cpu 解释执行代码:发现互斥锁还没释放等(切-剥夺cpu,强行释放gil)
      线程3。。。
      线程2。。。。
      垃圾线程抢到gil,当拿到cpu 解释执行代码:回收引用计数为0的数据.....
      线程1抢到gil,当拿到cpu 解释执行代码:改数据  放开互斥锁
      。。。。。。。。。。。。。。
    from threading import Thread,Lock
    import time
    mutex=Lock()
    count=0
    def task():
        global count
        mutex.acquire()
        temp=count
        time.sleep(0.1)
        count=temp+1
        mutex.release()
    if __name__ == '__main__':
        t_l=[]
        for i in range(2):
            t=Thread(target=task)
            t_l.append(t)
            t.start()
        for t in t_l:
            t.join()
        print('主',count)
  • 相关阅读:
    Android SDK、NDK、JNI的简单介绍
    深入理解计算机系统—异常
    Jmeter3.1 使用及新增报告功能
    jmeter3.1连接数据库报错,ORA00923: 未找到要求的 FROM 关键字
    Jenkins插件、war下载地址
    jenkins自动打tag
    jenkins参数化构建过程
    Jmeter接口测试自动化(jmeter+ant+jenkins持续集成)
    既然选择开始就不会停下
    知识提升整体
  • 原文地址:https://www.cnblogs.com/3sss-ss-s/p/9604988.html
Copyright © 2011-2022 走看看