python异步编程之asyncio初识

async 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执行权

async 实现协程示例

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

python 复制代码
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()

输出结果:

python 复制代码
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. 并发执行

协程基本原理

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

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

伪代码示例如下:

python 复制代码
任务列表 = [ 任务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完成再执行的耗时。

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

很多人可能会疑问,多线程遇到IO也会切换,为什么协程比线程性能好呢?

简单来是三点:

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

示例代码的高级api实现

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

python 复制代码
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

python 复制代码
loop = asyncio.get_event_loop()
task = loop.create_task(coro())
loop.run_until_complete(task)

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

相关推荐
独行soc3 小时前
2025年渗透测试面试题总结-18(题目+回答)
android·python·科技·面试·职场和发展·渗透测试
S01d13r4 小时前
gunicorn + flask 处理高并发请求
python·flask·gunicorn
杜子不疼.4 小时前
《Python列表和元组:从入门到花式操作指南》
开发语言·python
pan0c234 小时前
数据处理与统计分析 —— numpy入门
python·numpy
max5006004 小时前
基于桥梁三维模型的无人机检测路径规划系统设计与实现
前端·javascript·python·算法·无人机·easyui
秋氘渔5 小时前
综合案例:Python 函数知识整合 — 学生成绩管理系统
开发语言·python
AI 嗯啦6 小时前
SQL详细语法教程(三)mysql的函数知识
android·开发语言·数据库·python·sql·mysql
databook6 小时前
把数学对象画出来:Manim Mobject类库速查手册
python·数学·动效
图灵学术计算机论文辅导7 小时前
傅里叶变换+attention机制,深耕深度学习领域
人工智能·python·深度学习·计算机网络·考研·机器学习·计算机视觉
ruleslol7 小时前
python30-正则表达式
python·正则表达式