• python异步编程之asyncio初识


    image

    1|0async await介绍


    用asyncio提供的@asyncio.coroutine可以把一个生成器标记为协程类型,然后在协程内部用yield from 等待IO操作,让出cpu执行权。
    然而异步的关键字yield 和 yield from毕竟是复用生成器关键字,两者在概念上纠缠不清,所以从Python 3.5开始引入了新的语法async和await替换yield 和 yield from,让协程的代码更易懂。
    简单来说,可以这样理解:

    • async 替换 @asyncio.coroutine:标识一个函数为异步函数
    • await 替换 yield from:标识等待IO操作,让出CPU执行权

    2|0async 实现协程示例


    由于协程在各个python版本中有细微差异,本篇以python3.10为例

    import asyncio async def coro1(): print("start coro1") await asyncio.sleep(2) print("end coro1") async def coro2(): print("start coro2") await asyncio.sleep(1) print("end coro2") # 创建事件循环 loop = asyncio.get_event_loop() # 创建任务 task1 = loop.create_task(coro1()) task2 = loop.create_task(coro2()) # 运行协程 loop.run_until_complete(asyncio.gather(task1, task2)) # 关闭事件循环 loop.close()

    输出结果:

    start coro1 start coro2 end coro2 end coro1

    代码逻辑:

    1. 创建一个事件循环
    2. 将两个异步函数coro1,coro2封装成两个任务task1,task2
    3. 用asyncio.gather将两个任务组合到一起,并发执行task1,task2
    4. 先执行task1,遇到IO切换到task2
    5. 执行task2,遇到IO切换,但此时没有等待执行的任务,cpu为空
    6. task2执行完成,task1执行完成

    从示例代码可以看出,协程的几个关键要素:

    1. 事件循环
    2. 协程函数定义
    3. 可等待对象
    4. 并发执行

    3|0协程基本原理


    组成协程最重要的因素就是事件循环任务

    • 任务就是一个对象,包括执行的代码,执行完成、失败等状态以及返回结果,任务中通常会有IO切换。
    • 事件循环,可以把它当做是一个while循环。while循环在周期性的运行并执行一些任务,所有任务执行完成会关闭循环。

    伪代码示例如下:

    任务列表 = [ 任务1, 任务2, 任务3,... ] while True: 可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行''已完成'的任务返回 for 就绪任务 in 已准备就绪的任务列表: 执行已就绪的任务 for 已完成的任务 in 已完成的任务列表: 在任务列表中移除 已完成的任务 如果 任务列表 中的任务都已完成,则终止循环

    获取和创建事件循环:loop = asyncio.get_event_loop()
    驱动事件循环运行:loop.run_until_complete(asyncio.gather(task1, task2))
    事件循环过程:
    事件循环中执行任务,当执行到某一个任务时遇到IO时,协程会让出CPU给第二个任务执行,第二个任务中遇到IO再次让出CPU,直到所有任务完成。这就是协程并发性能好的一个关键能力:遇到IO切换任务执行,避免了程序等待IO完成再执行的耗时。

    4|0示例代码的高级api实现


    示例代码中使用了asyncio.get_event_loop()loop.run_until_complete()等代码,这些其实asyncio包的低级API,是为了展示底层原理而使用的。通常更推荐高级APIasyncio.run()实现协程并发。

    import asyncio async def coro1(): print("start coro1") await asyncio.sleep(2) print("end coro1") async def coro2(): print("start coro2") await asyncio.sleep(1) print("end coro2") async def main(): task1 = asyncio.create_task(coro1()) task2 = asyncio.create_task(coro2()) await asyncio.gather(task1, task2) asyncio.run(main())

    run() 从功能上等价于以下低阶API

    loop = asyncio.get_event_loop() task = loop.create_task(coro()) loop.run_until_complete(task)

    5|0为什么协程在IO密集时性能较好


    很多人可能会疑问,多线程遇到IO也会切换,为什么协程比线程性能好呢?
    简单来是三点:

    1. 协程更轻量级,切换需要恢复的上下文很少,所以比线程更快速
    2. 线程切换CPU是抢占的,协程是主动让出的,协程对CPU的使用更充分
    3. 协程更轻量级,启动线程需要的内存资源比协程更多

    连载一系列关于python异步编程的文章。包括同异步框架性能对比、异步事情驱动原理等。欢迎关注微信公众号第一时间接收推送的文章。


    __EOF__

    本文作者goldsunshine
    本文链接https://www.cnblogs.com/goldsunshine/p/17935368.html
    关于博主:评论和私信会在第一时间回复。或者直接私信我。
    版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
    声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
  • 相关阅读:
    深入了解苹果证书及其分类,提升iOS应用开发效率
    这款信创国产化传输方案 安全又可靠!
    TN、HTN、STN、FSTN、DSTN、CSTN、TFT、LCD 的区别
    Python numpy | 详解 np.unique() 的妙用 去重 + 重排序、统计出现次数
    取消注册Single Sign-on 域中的另一台vCenter
    南大通用GBase8s 常用SQL语句(237)
    怎么恢复电脑删除的文件?文件恢复,主要看这几个方法
    为什么学不会俄语?
    petalinux中加入驱动模块
    xray扫描器的使用长亭xray被动扫描
  • 原文地址:https://www.cnblogs.com/goldsunshine/p/17935368.html