• 全局解释锁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解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势

     前言: 

    - GIL其实就是一把互斥锁(牺牲了效率但是保证了数据的安全)。
    - 线程是执行单位,但是不能直接运行,需要先拿到python解释器解释之后才能被cpu执行
    - 同一时刻同一个进程内多个线程无法实现并行,但是可以实现并发
    

      一.GIL全局解释器 垃圾回收机制:

    - 垃圾回收机制也是一个任务,跟你的代码不是串行运行,如果是串行会明显有卡顿
    - 这个垃圾回收到底是开进程还是开线程?肯定是线程,线程肯定也是一段代码,所以想运行也必须要拿到python解释器
    没有GIL全局解释器锁 他只是对线程加锁 不是对数据
    运行垃圾回收机制:引用计数 1,必须先拿到python 解释器---> 2.python 进程下的多个线程是并发。若此时你想创建一个  a = 1 cpu运行速度是非常快的 
    那么就会引发 其他线程垃圾回收机制扫描把我刚创建的内存清理掉 所以必须设置GIL全局解释器锁
    也就意味着在Cpython解释器上有一把GIL全局解释器锁

     二. 

    1.python中的多线程到底有没有用?
      一、数据密集型
      二、IO密集型

    #### 1.python中的多线程到底有没有用?
    
    单核情况下:四个任务
    
    多核情况下:四个任务
    
    计算密集型:一个任务算十秒,四个进程和四个线程,肯定是进程快
    
    IO密集型:任务都是纯io情况下,线程开销比进程小,肯定是线程好
    
    一、数据密集型
    def task(): res = 0 for i in range(100000000): res = res*i if __name__ == '__main__': print(os.cpu_count()) #本机内核 p_list=[] start_time= time.time() for i in range(4): p = Process(target=task) # 进程运行时间为10.636553287506104 # p = Thread(target= task) # 线程运行时间为19.97660756111145 p.start() p_list.append(p) for p in p_list: p.join() end_time = time.time() print('运行时间为%s'% (end_time-start_time))

      

    ""
    二、IO密集型
    def work():
        time.sleep(3)
    if __name__ == '__main__':
        print(os.cpu_count())
        start_time =time.time()
        p_list=[]
        for i in range(4):
            # p = Process(target= work)   # run is total_time7.271259546279907
            p = Thread(target= work)    # run is total_time3.002392053604126
            p.start()
            p_list.append(p)
        for p in p_list:
            p.join()
        end_time =time.time()
        print('run is total_time%s'%(end_time-start_time))
    

      三、全局锁与普通锁

      

    对于不同的数据,要想保证安全,需要加不同的锁处理
    GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
    保证的是同一个进程下多个线程之间的安全
    

      

    """
    from threading import Thread
    
    import os
    import time
    from threading import Lock
    mutex = Lock()
    num = 100
    def task():
        global num
        mutex.acquire()     #抢锁
        temp = num
        time.sleep(0.1)
        num = temp-1
        mutex.release()  # 释放锁 开始一个
    
    
    if __name__ == '__main__':
        p_lsit=[]
        for i in range(10):
    
            p = Thread(target=task)
            p.start()
            p_lsit.append(p)
        for p in p_lsit:
            p.join()
        print(num)  # 90 相当于10个线程同时去抢100票 必须要确保一个数据同时被10个进程同时抢 锁是起到保护作用 取完一个减一个
    

      四、.死锁与递归锁(了解)

    自定义锁一次acquire必须对应一次release,不能连续acquire

    递归锁可以连续的acquire,每acquire一次计数加一

    import time
    from threading import Thread,RLock
    
    mutexA = mutexB= RLock()    # 递归锁RLock
    class Mythread(Thread):
    
        def run(self):
            self.fn1()
            self.fn2()
    
        def fn1(self):
            #设置锁
            mutexA.acquire()
            print('%s 抢到A锁了'%self.name)
            mutexB.acquire()
            print('%s 抢到B锁了'%self.name)
            mutexB.release()
            print('%s释放了B锁'%self.name)
            mutexA.release()
            print('%s释放了A锁'%self.name)
    
        def fn2(self):
            mutexB.acquire()
            print('%s 抢到A锁了' % self.name)
            time.sleep(1)
            mutexA.acquire()
            print('%s 抢到B锁了' % self.name)
            mutexA.release()
            print('%s释放了B锁' % self.name)
            mutexB.release()
            print('%s释放了A锁' % self.name)
    
    
    if __name__ == '__main__':
        for i in range(100):
            t = Mythread()
            t.start()
    

      

    五.Event事件

    一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样

    from threading import Thread
    
    from threading import Event
    
    import time
    event = Event()    #造了一个绿灯
    def light():
        print('等红灯')
        time.sleep(3)
        event.set() # 解除阻塞并且给 event发了一个信号
        print('绿灯亮了')
    
    def car(i):
        print('%s灯红灯'%i)
        event.wait()
        print('%s绿灯了,加油门'%i)
    
    if __name__ == '__main__':
        t = Thread(target=light)
        t.start()
    
        p_list=[]
        for i in range(5):
            p = Thread(target=car,args=(i,))
            p.start()
    
    # 等红灯
    # 0灯红灯
    # 1灯红灯
    # 2灯红灯
    # 3灯红灯
    # 4灯红灯
    # 绿灯亮了
    # 0绿灯了,加油门
    # 1绿灯了,加油门
    # 3绿灯了,加油门
    # 4绿灯了,加油门
    # 2绿灯了,加油门#
    

      

    六.信号量(了解)

    自定义的互斥锁如果是一个厕所,那么信号量就相当于公共厕所,门口挂着多个厕所的钥匙。抢和释放跟互斥锁一致

    普通互斥锁, 独立卫生间 所有人只有一把锁
    信号量 ,公共卫生间 有多少个坑 就有多少把锁
    """
    from threading import Thread
    from threading import Semaphore #信号量
    import time
    import random
    sm = Semaphore(5)    #一个公共厕所造了5个坑 在厕所外放了5把锁
    
    def task(name):
        sm.acquire()    # 释放信号锁
    
        print('%s正在蹲坑'%name)
        time.sleep(random.randint(1, 3))
        sm.release()
    
    for i in range(20):
        t = Thread(target= task,args=('%s伞兵'%i,))
        t.start()
    

     

    0伞兵正在蹲坑
    1伞兵正在蹲坑
    2伞兵正在蹲坑
    3伞兵正在蹲坑
    4伞兵正在蹲坑
    此时5个人中 有一个人好了 同时释放了一把锁
    5伞兵正在蹲坑
    前面5个好了两个释放给 6,7
    6伞兵正在蹲坑
    7伞兵正在蹲坑
    
    8伞兵正在蹲坑
    9伞兵正在蹲坑
    10伞兵正在蹲坑
    11伞兵正在蹲坑
    
    12伞兵正在蹲坑
    13伞兵正在蹲坑
    14伞兵正在蹲坑
    15伞兵正在蹲坑
    
    16伞兵正在蹲坑
    17伞兵正在蹲坑
    18伞兵正在蹲坑
    19伞兵正在蹲坑
    

      

      

    七.线程queue

    同一个进程下的线程数据都是共享的为什么还要用queue?queue本身自带锁的功能,能够保证数据的安全

      

    import queue
    
    """
    1.普通q
    2.堆栈。先进后出q
    3.优先级q
    
    """
    q = queue.Queue(3)
    q.put(1)
    q.put(2)
    
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())  # 取值
    ——————》》》
    1
    2
    3 q = queue.LifoQueue(5) q.put(1) q.put(2) q.put(3) q.put(4) q.put(5) print(q.get()) print(q.get()) print(q.get()) print(q.get()) print(q.get()) # 先进后出 和堆栈一样
    ——————》》》

    5
    4
    3
    2
    1

    q = queue.PriorityQueue(3)
    q.put(100,"q")
    q.put(20,"t")
    q.put(-1,'s')
    
    print(q.get())
    print(q.get())
    print(q.get())  # 优先级是按照数字从小到大排列的
    ————————》》》

    -1
    20
    100

      

      

  • 相关阅读:
    c++常用库
    boost
    android
    UITableView 多选
    c++ 比较两个集合
    事件加不上的另一种原因
    ios多线程
    ubuntu android
    jna StdCallCallback 回调问题查证
    java
  • 原文地址:https://www.cnblogs.com/mofujin/p/10833934.html
Copyright © 2020-2023  润新知