zoukankan      html  css  js  c++  java
  • day30

    GIL全局解释器

    GIL本质就是一把互斥锁,既然是互斥锁,所有互斥锁的本质都一样,都是将并发运行变成串行,以此来控制同一时间内共享数据只能被一个任务所修改,进而保证数据安全。

    要想了解GIL,首先确定一点:每次执行python程序,都会产生一个独立的进程。例如python test.py,python aaa.py,python bbb.py会产生不同的python进程

    基于Cpython来研究全局解释锁

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

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

    ​ 单个进程下的多个线程无法实现并行,但能实现并发

    ​ 3.这把锁主要是因为CPython的内存管理不是“线程安全”的

    ​ 内存管理

    ​ 垃圾回收机制

    ​ GIL的存放就是为了保证线程安全的

    ​ 注意:多线程过来执行,一旦遇到IO操作,就会立马释放GIL解释器,交给下一个先进来的进程

    import time
    from threading import Thread,current_thread
    number = 100
    def task():
        global number
        number2 = number
        number = number2 - 1
        print(number,current_thread().name)
    for line in range(100):
        t = Thread(target=task)
        t.start()
    

    验证多线程的作用

    多线程的作用:

    ​ 站在两个角度去看问题:

    ​ 四个任务,计算密集型,每个任务需要10s

    ​ 单核:

    ​ 开启进程

    ​ 4个进程:40s

    ​ 开启线程

    ​ 消耗资源远小于进程

    ​ 4个线程:40s

    ​ 多核:

    ​ 开启进程

    ​ 并行执行,效率比较高

    ​ 4个进程:10s

    ​ 开启线程

    ​ 并发执行,执行效率低

    ​ 4个线程:40s

    ​ 四个任务,IO密集型,每个任务需要10s

    ​ 单核:

    ​ 开启进程

    ​ 消耗资源过大

    ​ 4个进程:40s

    ​ 开启线程

    ​ 消耗资源远小于进程

    ​ 4个线程:40s

    ​ 多核:

    ​ 开启进程
    ​ 并行执行,效率小于多线程,因为遇到IO立马切换CPU的执行权限

    ​ 4个进程:40s+开启进程消耗的额外时间

    ​ 4个线程:40s

    在计算密集的情况下:

    ​ 使用多进程

    在IO密集型的情况下:

    ​ 使用多线程

    高效执行多进程内多个IO密集型的程序:

    ​ 使用 多进程+多线程

    应用:

    ​ 多线程用于IO密集型,如socket,爬虫,web

    ​ 多进程用于计算密度型,如金融分析

    死锁现象

    ​ 所谓死锁:指的是两个或两个以上的进程或线程在执行过程中,因为夺资源而造成的一种互相等待的现象,若无外力作用,它们都无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永久在互相等待的进程称为死锁进程。

    from threading import Lock as Lock
    import time
    mutexA  = Lock()
    mutexA.acquire()
    mutexA.acquire()
    print(123)
    mutexA.release()
    mutexA.release()
    

    解决死锁的方法:递归,在python中为了支持在同一线程中多次请求同一资源,python提供了可重入锁RLock。

    这个RLock内部维护这一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果用RLock,则不会发生死锁。

    典型问题:科学家吃面

    死锁问题

    #死锁的问题
    import time
    from threading import Thread,Lock
    noodle_lock = Lock()
    fork_lock = Lock()
    def eat1(name):
        noodle_lock.acquire()
        print('%s 抢到了面条'%name)
        fork_lock.acquire()
        print('%s 抢到了叉子'%name)
        print('%s 吃面'%name)
        fork_lock.release()
        noodle_lock.release()
    
    def eat2(name):
        fork_lock.acquire()
        print('%s 抢到了叉子' % name)
        time.sleep(1)
        noodle_lock.acquire()
        print('%s 抢到了面条' % name)
        print('%s 吃面' % name)
        noodle_lock.release()
        fork_lock.release()
    
    for name in ['哪吒','nick','tank']:
        t1 = Thread(target=eat1,args=(name,))
        t2 = Thread(target=eat2,args=(name,))
        t1.start()
        t2.start()
    
    递归锁解决死锁问题
    import time
    from threading import Thread,RLock
    fork_lock = noodle_lock = RLock()
    def eat1(name):
        noodle_lock.acquire()
        print('%s 抢到了面条'%name)
        fork_lock.acquire()
        print('%s 抢到了叉子'%name)
        print('%s 吃面'%name)
        fork_lock.release()
        noodle_lock.release()
    
    def eat2(name):
        fork_lock.acquire()
        print('%s 抢到了叉子' % name)
        time.sleep(1)
        noodle_lock.acquire()
        print('%s 抢到了面条' % name)
        print('%s 吃面' % name)
        noodle_lock.release()
        fork_lock.release()
    
    for name in ['哪吒','nick','tank']:
        t1 = Thread(target=eat1,args=(name,))
        t2 = Thread(target=eat2,args=(name,))
        t1.start()
        t2.start()
    

    递归锁

    递归锁(了解)
    用于解决死锁问题
    RLock:比喻成万能钥匙,可以提供给多个人去使用
    但是第一个使用的时候,会对该锁做一个引用计数
    只有引用计数为0时,才能真正释放让另一个去使用

    from threading import RLock,Thread,Lock
    import time
    mutex_a  = 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')
            #IO操作
            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,Lock
    from threading import current_thread
    from threading import Thread
    import time
    sm = Semaphore(5)
    mutex = Lock()
    
    def task():
        sm.acquire()
        print(f'{current_thread().name}执行任务')
        time.sleep(1)
        sm.release()
    for line in range(20):
        t= Thread(target=task)
        t.start()
    

    线程队列

    线程Q(了解级别1):线程队列 面试会问:FIFO

    ​ FIFO队列:先进先出

    ​ LIFO队列:后进先出

    ​ 优先级队列:根据参数内,数字的大小进行分级,数字值越小,优先级越高

    import queue
    普通的线程队列:先进先出
    q = queue.Queue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())  #1
    
    LIFO队列:后进先出
    q = queue.LiFoQueue()
    q.put(1)
    q.put(2)
    q.put(3)
    print(q.get())#3
    
    优先级队列
    q=queue.PriorityQueue()
    #若参数中传的是元组,会以元组中第一个数字参数为准
    q.put(('a优','娃娃头',4))#a==97  ascll码值
    q.put(('b优','娃娃头',4))#b==98
    
    1.首先根据第一个参数判断ascll表的数值大小
    2.判断第几个参数中的汉字顺序
    3.在判断第二个参数中的数字————》字符串数字---》中文
    4.以此类推
    
    

    current_thread:
    返回当前线程对象,对应于调用方的控制线程。

    如果调用者的控制线程不是通过线程创建的
    模块,返回一个功能有限的虚拟线程对象。

  • 相关阅读:
    尚硅谷前端2020Web前端学习记录
    阿里网盘阿里云网盘内测资格获取,阿里网盘开通
    冰眼冷链物流监控平台-2020微服务项目实战
    探花交友智能推荐社交项目-2020Java大数据实战
    互联网Java工程师面试突击三季
    恋上数据结构与算法(一、二、三季)
    布客·ApacheCN 编程/后端/大数据/人工智能学习资源 2020.9
    NumPy 基础知识·翻译完成
    NumPy 初学者指南中文第三版·翻译完成
    NumPy 秘籍中文第二版·翻译完成
  • 原文地址:https://www.cnblogs.com/gfhh/p/11728104.html
Copyright © 2011-2022 走看看