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, '你')
    
  • 相关阅读:
    垂死挣扎还是涅槃重生 -- Delphi XE5 公布会归来感想
    自考感悟,话谈备忘录模式
    [每日一题] OCP1z0-047 :2013-07-26 alter table set unused之后各种情况处理
    Java实现 蓝桥杯 算法提高 p1001
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 拿糖果
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 求arccos值
    Java实现 蓝桥杯 算法提高 因式分解
    Java实现 蓝桥杯 算法提高 因式分解
  • 原文地址:https://www.cnblogs.com/setcreed/p/11729265.html
Copyright © 2011-2022 走看看