zoukankan      html  css  js  c++  java
  • GIL全局解释器锁、死锁、递归锁、线程队列

    GIL全局解释锁

    1. GIL本质上是一个互斥锁。

    2. GIL是为了阻止同一个进程内多个进程同时执行(并行)

      • 单个进程下的多个线程无法实现并行,但能实现并发
    3. 这把锁主要是因为Cpython的内存管理不是线程安全的

      • 保证线程在执行任务时不会被垃圾回收机制回收
    from threading import Thread
    import time
    
    num = 100
    
    
    def task():
        global num
        num2 = num
        time.sleep(1)
        num = num2 - 1
        print(num)
    
    
    for line in range(100):
        t = Thread(target=task)
        t.start()
        
    # 这里的运行结果都是99, 加了IO操作,所有线程都对num进行了减值操作,由于GIL锁的存在,没有修改成功,都是99
    

    多线程的作用

    1. 计算密集型, 有四个任务,每个任务需要10s

    单核:

    • 开启进程
      • 消耗资源过大
      • 4个进程: 40s
    • 开启线程
      • 消耗资源远小于进程
      • 4个线程: 40s

    多核:

    • 开启进程
      • 并行执行, 效率比较高
      • 4个进程: 10s
    • 开启线程
      • 并发执行,执行效率低
      • 4个线程: 40s
    1. IO密集型, 四个任务, 每个任务需要10s

    单核:

    • 开启进程
      • 消耗资源过大
      • 4个进程: 40s
    • 开启线程
      • 消耗资源远小于进程
      • 4个线程: 40s

    多核:

    • 开启进程
      • 并行执行, 效率小于多线程, 但是遇到IO会立马切换CPU的执行权限
      • 4个进程: 40s + 开启进程消耗的额外时间
    • 开启线程
      • 并发执行,执行效率高于多进程
      • 4个线程: 40s

    测试计算密集型

    from threading import Thread
    from multiprocessing import Process
    import time
    import os
    
    
    # 计算密集型
    def work1():
        number = 0
        for line in range(100000000):
            number += 1
    
    # IO密集型
    def work2():
        time.sleep(2)
    
    
    if __name__ == '__main__':
        # 测试计算密集型
        print(os.cpu_count())   # 4核cpu
    
        start = time.time()
        list1 = []
        for line in range(6):
    
            p = Process(target=work1)  # 程序执行时间8.756593704223633
            # p = Thread(target=work1)   # 程序执行时间31.78555393218994
            list1.append(p)
            p.start()
        for p in list1:
            p.join()
        end = time.time()
        print(f'程序执行时间{end - start}')
    

    IO密集型

    from threading import Thread
    from multiprocessing import Process
    import time
    import os
    
    # 计算密集型
    def work1():
        number = 0
        for line in range(100000000):
            number += 1
    
    # IO密集型
    def work2():
        time.sleep(1)
    
    
    if __name__ == '__main__':
        # 测试计算密集型
        print(os.cpu_count())   # 4核cpu
    
        start = time.time()
        list1 = []
        for line in range(100):
    
            # p = Process(target=work2)  # 程序执行时间15.354223251342773
            p = Thread(target=work2)   # 程序执行时间1.0206732749938965
            list1.append(p)
            p.start()
        for p in list1:
            p.join()
        end = time.time()
        print(f'程序执行时间{end - start}')
    

    结论:

    • 在计算密集型的情况下, 使用多进程
    • 在IO密集型的情况下, 使用多线程
    • 高效执行多个进程, 内有多个IO密集型程序,使用多进程 + 多线程

    死锁现象

    指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如无外力作用,它们都无法推进下去.此时称系统处于死锁状态

    以下就是死锁:

    from threading import Thread, Lock
    from threading import current_thread
    import time
    
    mutex_a = Lock()
    mutex_b = Lock()
    
    
    class MyThread(Thread):
    
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutex_a.acquire()
            print(f'用户{self.name}抢到锁a')
            mutex_b.acquire()
            print(f'用户{self.name}抢到锁b')
            mutex_b.release()
            print(f'用户{self.name}释放锁b')
            mutex_a.release()
            print(f'用户{self.name}释放锁a')
    
        def func2(self):
            mutex_b.acquire()
            print(f'用户{self.name}抢到锁b')
            time.sleep(1)
            mutex_a.acquire()
            print(f'用户{self.name}抢到锁a')
            mutex_a.release()
            print(f'用户{self.name}释放锁a')
            mutex_b.release()
            print(f'用户{self.name}释放锁b')
    
    
    for line in range(10):
        t = MyThread()
        t.start()
        
        
    '''
    用户Thread-1抢到锁a
    用户Thread-1抢到锁b
    用户Thread-1释放锁b
    用户Thread-1释放锁a
    用户Thread-1抢到锁b
    用户Thread-2抢到锁a
    '''
    # 一直等待
    

    递归锁

    用于解决死锁问题

    RLock: 比喻成万能钥匙,可以提供给多个人使用

    但是第一个使用的时候,会对该锁做一个引用计数

    只有引用计数为0, 才能真正释放让一个人使用

    上面的例子中用RLock代替Lock, 就不会发生死锁现象

    from threading import Thread, Lock, RLock
    from threading import current_thread
    import time
    
    # mutex_a = Lock()
    # mutex_b = Lock()
    
    mutex_a = mutex_b = RLock()
    
    class MyThread(Thread):
    
        def run(self):
            self.func1()
            self.func2()
    
        def func1(self):
            mutex_a.acquire()
            print(f'用户{self.name}抢到锁a')
            mutex_b.acquire()
            print(f'用户{self.name}抢到锁b')
            mutex_b.release()
            print(f'用户{self.name}释放锁b')
            mutex_a.release()
            print(f'用户{self.name}释放锁a')
    
        def func2(self):
            mutex_b.acquire()
            print(f'用户{self.name}抢到锁b')
            time.sleep(1)
            mutex_a.acquire()
            print(f'用户{self.name}抢到锁a')
            mutex_a.release()
            print(f'用户{self.name}释放锁a')
            mutex_b.release()
            print(f'用户{self.name}释放锁b')
    
    
    for line in range(10):
        t = MyThread()
        t.start()
    

    信号量(了解)

    互斥锁: 比喻成一个家用马桶, 同一时间只能让一个人去使用

    信号比喻成公测多个马桶: 同一时间可以让多个人去使用

    from threading import Semaphore
    from threading import Thread
    from threading import current_thread
    import time
    
    sm = Semaphore(5)
    
    def task():
        sm.acquire()
        print(f'{current_thread().name}执行任务')
        time.sleep(1)
        sm.release()
    
    
    for i in range(20):
        t = Thread(target=task)
        t.start()
    

    线程队列

    线程Q: 就是线程队列 FIFO

    • 普通队列: 先进先出 FIFO
    • 特殊队列: 后进先出 LIFO
    • 优先级队列: 若传入一个元组,会依次判断参数的ASCII的数值大小
    import queue
    
    # 普通的线程队列: 遵循先进先出
    q = queue.Queue()
    q.put(1)
    q.put(2)
    q.put(3)
    
    print(q.get())  # 1
    print(q.get())  # 2
    
    
    # LIFO队列  后进先出
    q = queue.LifoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())  # 3
    
    
    # 优先级队列:根据参数内
    q = queue.PriorityQueue()
    q.put((4, '我'))
    q.put((2, '你'))
    q.put((3, 'ta'))
    print(q.get())   # (2, '你')
    
  • 相关阅读:
    python 数据可视化(一)
    python unittest自动测试框架
    使用selenium抓取淘宝信息并存储mongodb
    python之re模块(正则表达式)
    [原] OpenGL ES 学习笔记 (二)
    [原] OpenGL ES 学习笔记 (一)
    [转] iOS开发同学的arm64汇编入门
    [转]ARM64 汇编
    [转]iOS高级调试&逆向技术-汇编寄存器调用
    [转] CGTime CMTimeRange CMTimeMapping 小结
  • 原文地址:https://www.cnblogs.com/setcreed/p/11729265.html
Copyright © 2011-2022 走看看