• 对全局解释器锁GIL的一些理解


    GIL

    为了避免多线程竞争导致的数据安全性问题。CPython 引入GIL全局解释器锁,使得任何时刻仅有一个线程在执行。即便在多核心处理器上,使用 GIL 的解释器也只允许同一时间执行一个线程。缺点就是使得多核处理器退化成单核处理器,多线程的执行效率很不好

    yeild和生成器

    使用了yield关键字的函数不再是函数,而是生成器。

    • 代码执行到yield会暂停,然后把结果返回出去,下次启动生成器会在暂停的位置继续往下执行
    • 生成器(generator)能够迭代的关键是它有一个next()方法,通过反复调用next()方法,直到捕获一个异常。
    • 每次启动生成器都会返回一个值,多次启动可以返回多个值,也就是yield可以返回多个值, 而return只能返回一次值。
    • 第一次调用时必须先next()或send(None),否则会报错,send后之所以为None是因为这时候没有上一个yield,可以认为next()等同于send(None),更加推荐使用next()。
    • send(msg)与next()的区别在于send可以传递参数给yield表达式,这时传递的参数会作为yield表达式的值,而yield的参数是返回给调用者的值。——换句话说,就是send可以强行修改上一个yield表达式值。比如函数中有一个yield赋值,a = yield 5,第一次迭代到这里会返回5,a还没有赋值。第二次迭代时,使用.send(10),那么,就是强行修改yield 5表达式的值为10,本来是5的,那么a=10

    协程

    协程可以简单为轻量级线程,由于是在用户态下实现的,也叫用户态线程

    内核并没有实现协程,协程是语言层面自己实现的,不同语言实现方案不一样,py中是利用yeild实现

    具体实现如下:

    多线程之间的同步靠GIL,而多个协程在一个线程内,同步依靠的是生成器,你执行时我等待,我执行时你等待、

    协程相对线程的优势:

    • 最大的优势就是协程极高的执行效率。因为多个协程在同一个线程内,所以协程之间的切换开销很小。相比线程之间的切换开销要小的多。

    • 第二大优势就是不需要加锁, 因为多个协程在同一个线程内,所以无需加锁。

    怎么选择?
    IO密集型适合用多协程
    cpu密集型适合多进程(因为进程可以利用多核,py里面线程和协程都不能利用)

    协程的使用:

    
    import asyncio
    
    async def worker_1():
        print('worker_1 start')
        await asyncio.sleep(1)
        print('worker_1 done')
    
    async def worker_2():
        print('worker_2 start')
        await asyncio.sleep(2)
        print('worker_2 done')
    
    async def main():
        task1 = asyncio.create_task(worker_1())
        task2 = asyncio.create_task(worker_2())
        print('before await')
        await task1
        print('awaited worker_1')
        await task2
        print('awaited worker_2')
    
    %time asyncio.run(main())
    
    ########## 输出 ##########
    
    before await
    worker_1 start
    worker_2 start
    worker_1 done
    awaited worker_1
    worker_2 done
    awaited worker_2
    Wall time: 2.01 s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    剖析上面代码的运行原理:
    1、asyncio.run(main()),程序进入 main() 函数,事件循环开启;
    2、task1 和 task2 任务被创建,并进入事件循环等待运行;运行到 print,输出 ‘before await’;
    3、await task1 执行,用户选择从当前的主任务中切出,事件调度器开始调度 worker_1;
    4、worker_1 开始运行,运行 print 输出’worker_1 start’,然后运行到 await asyncio.sleep(1), 从当前任务切出,事件调度器开始调度 worker_2;
    5、worker_2 开始运行,运行 print 输出 ‘worker_2 start’,然后运行 await asyncio.sleep(2) 从当前任务切出;
    6、以上所有事件的运行时间,都应该在 1ms 到 10ms 之间,甚至可能更短,事件调度器从这个时候开始暂停调度;
    7、一秒钟后,worker_1 的 sleep 完成,事件调度器将控制权重新传给 task_1,输出 ‘worker_1 done’,task_1 完成任务,从事件循环中退出;
    8、await task1 完成,事件调度器将控制器传给主任务,输出 ‘awaited worker_1’,·然后在 await task2 处继续等待;
    9、两秒钟后,worker_2 的 sleep 完成,事件调度器将控制权重新传给 task_2,输出 ‘worker_2 done’,task_2 完成任务,从事件循环中退出;
    10、主任务输出 ‘awaited worker_2’,协程全任务结束,事件循环结束。

    生产者消费者

    
    import asyncio
    import random
    
    async def consumer(queue, id):
        while True:
            val = await queue.get()
            print('{} get a val: {}'.format(id, val))
            await asyncio.sleep(1)
    
    async def producer(queue, id):
        for i in range(5):
            val = random.randint(1, 10)
            await queue.put(val)
            print('{} put a val: {}'.format(id, val))
            await asyncio.sleep(1)
    
    async def main():
        queue = asyncio.Queue()
    
        consumer_1 = asyncio.create_task(consumer(queue, 'consumer_1'))
        consumer_2 = asyncio.create_task(consumer(queue, 'consumer_2'))
    
        producer_1 = asyncio.create_task(producer(queue, 'producer_1'))
        producer_2 = asyncio.create_task(producer(queue, 'producer_2'))
    
        await asyncio.sleep(10)
        consumer_1.cancel()
        consumer_2.cancel()
        
        await asyncio.gather(consumer_1, consumer_2, producer_1, producer_2, return_exceptions=True)
    
    asyncio.run(main())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    【boot loader】Aurix TC3xx用户手册解读--CAN Bootstrap loader
    少数据量情况下的深度学习模型训练效果提升技巧
    ARP欺骗攻击实操
    DJ11 8086系列处理器(第二节课)
    (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
    Python+Requests+Pytest+Excel+Allure 接口自动化测试项目实战【框架之间的对比】
    【TFS-CLUB社区 第5期赠书活动】〖Python OpenCV从入门到精通〗等你来拿,参与评论,即可有机获得
    量子笔记:全局相位、相对相位、布洛赫球面
    css基本选择器
    1B踩坑大王
  • 原文地址:https://blog.csdn.net/qq_40337086/article/details/125577677