• 全局解释器锁及其他用法


    一、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解释器中,同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势
    1、存在原因:
    GIL是一个互斥锁:保证数据的安全(以牺牲效率来换取数据的安全)
    阻止同一个进程内多个线程同时执行(不能并行但能并发)
    GIL全局解释器存在的原因是Cpython解释器的内存管理不是线程安全的
    同一个进程下的多个线程不能实现并行但是能够实现并发,多个进程下的线程能够实现并行
    2、多线程和多进程都有自己的优点,要根据项目需求合理选择
    四个任务:计算密集的任务 每个任务耗时10s
    单核情况下:
    多线程好一点,消耗的资源少一点
    多核情况下:
    开四个进程:10s多一点
    开四个线程:40s多一点

    四个任务:IO密集的任务 每个任务io 10s
    单核情况下:
    多线程好一点
    多核情况下:
    多线程好一点

    二、GIL全局解释器锁与普通锁的互斥锁的区别
    对于不同的数据,要想保证数据的安全,需要加不同的锁处理
    GIL并不能保证数据的安全,它是对Cpython解释器加锁,针对的是线程
    保证的是同一个进行下多个线程之间的安全

    三、死锁与递归锁
    自定义锁一次acquire必须对应一次release,不能连续acquire
    递归锁可以连续acquire,每acquire一次计数加一(针对的是第一个抢到的)
    1、死锁
    from threading import Thread,Lock
    import time
    
    
    mutexA = Lock()
    mutexB = Lock()
    
    
    class MyThread(Thread):
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            mutexA.acquire()
            print('%s 抢到A锁了' % self.name)
            mutexB.acquire()
            print('%s 抢到B锁了' % self.name)  # B锁在1手里,拿不到,阻塞
            mutexB.release()
            print('%s 释放了B锁' % self.name)
            mutexA.release()
            print('%s 释放了A锁' % self.name)
    
        def f2(self):
            mutexB.acquire()
            print('%s 抢到B锁了' % self.name)    # 1抢到了B锁,这时2抢到了A锁
            time.sleep(1)
            mutexA.acquire()
            print('%s 抢到A锁了' % self.name)    # A锁在2手里,拿不到,阻塞
            mutexA.release()
            print('%s 释放了A锁' % self.name)
            mutexB.release()
            print('%s 释放了B锁' % self.name)
    
    
    for i in range(100):
        t = MyThread()
        t.start()
    2、递归锁
    from threading import Thread,RLock
    import time
    
    
    mutexA = mutexB = RLock()
    
    
    
    class MyThread(Thread):
        def run(self):
            self.f1()
            self.f2()
    
        def f1(self):
            mutexA.acquire()
            print('%s 抢到A锁了' % self.name)  # 计数加1
            mutexB.acquire()
            print('%s 抢到B锁了' % self.name)  # 计数加1,变为2
            mutexB.release()
            print('%s 释放了B锁' % self.name)  # 计数减一
            mutexA.release()
            print('%s 释放了A锁' % self.name)  # 计数变为0
    
        def f2(self):
            mutexB.acquire()
            print('%s 抢到B锁了' % self.name)    # 计数又变1
            time.sleep(1)                        # 所有线程都起来,但是发现计数为1,等待在原地
            mutexA.acquire()
            print('%s 抢到A锁了' % self.name)
            mutexA.release()
            print('%s 释放了A锁' % self.name)
            mutexB.release()
            print('%s 释放了B锁' % self.name)
    
    
    for i in range(100):
        t = MyThread()
        t.start()
    四、信号量
    信号量就是规定了有多少把钥匙,所有人抢多把锁
    from threading import Thread,Semaphore
    import time
    import random
    
    sm = Semaphore(5)    # 五把锁
    
    
    def task(name):
        sm.acquire()        # 同时有五个人
        print('%s 正在上厕所' % name)
        time.sleep(random.randint(1,3))
        sm.release()       # 释放几把锁就抢几把
    
    
    if __name__ == '__main__':
        for i in range(20):
            t = Thread(target=task,args=(i,))
            t.start()
    五、event事件
    一些线程需要等待另外一些线程运行完毕才能运行,类似于发射信号一样
    from threading import Event,Thread
    import time
    
    event = Event()
    
    
    def light():
        print('红灯正亮着')
        time.sleep(3)
        event.set()      #  给event发了一个信号,解除阻塞
        print('绿灯亮了')
    
    
    def car(i):
        print('%s 正在等红灯'%i)
        event.wait()            # 阻塞,在等event的信号
        print('%s起飞了'%i)
    
    
    t1 = Thread(target=light)
    t1.start()
    
    for i in range(10):
        t = Thread(target=car,args=(i,))
        t.start()
    六、线程queue:导入模块queue
    1、普通q:正常放取,满了再放会阻塞在原地,一个都取不到,空了再取会阻塞在原地
    q = queue.Queue(3)
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())
    print(q.get())
    print(q.get())
    2、先进后出:每次取都是取的队列中最后一个,没有再取会阻塞在原地
    q = queue.LifoQueue(5)
    q.put(1)
    q.put(2)
    q.put(3)
    q.put(4)
    q.put(5)
    print(q.get())
    3、优先级:优先取出最小的那个数
    q = queue.PriorityQueue()
    q.put((10,'a'))
    q.put((-1,'a'))
    q.put((100,'a'))
    print(q.get())
    print(q.get())
    print(q.get())
  • 相关阅读:
    菜鸟浅谈软件开发项目管理
    中国准货币体系的概要简析
    使用dockercompose安装wordpress
    货币乘数
    安全测试的相关内容
    TCP三次握手和四次挥手
    HTTP协议相关
    描述浏览器登录的过程
    AJAX相关知识
    什么是热钱
  • 原文地址:https://www.cnblogs.com/yanminggang/p/10832514.html
Copyright © 2020-2023  润新知