python协程异步IO中asyncio的使用

 更新时间:2023年12月30日 10:53:16   作者:金色旭光  
这篇文章主要介绍了python异步编程之asyncio的使用,python中异步IO操作是通过asyncio来实现的,为了更加详细说明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为例

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

代码逻辑:

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

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

  • 事件循环
  • 协程函数定义
  • 可等待对象
  • 并发执行

协程基本原理

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

  • 任务就是一个对象,包括执行的代码,执行完成、失败等状态以及返回结果,任务中通常会有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完成再执行的耗时。

示例代码的高级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)

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

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

简单来是三点:

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

以上就是python协程异步IO中asyncio的使用的详细内容,更多关于python asyncio的资料请关注脚本之家其它相关文章!

相关文章

  • python笔记(2)

    python笔记(2)

    python笔记,参考上一篇文章大家继续
    2012-10-10
  • python里反向传播算法详解

    python里反向传播算法详解

    在本篇文章了小编给大家整理的是一篇关于python里反向传播算法详解内容,有兴趣的朋友们可以学习下。
    2020-11-11
  • phpsir 开发 一个检测百度关键字网站排名的python 程序

    phpsir 开发 一个检测百度关键字网站排名的python 程序

    一个检测百度关键字网站排名的python 程序 phpsir 开发
    2009-09-09
  • Python 中 Meta Classes详解

    Python 中 Meta Classes详解

    首先,在认识metaclass之前,你需要认识下python中的class。python中class的奇怪特性借鉴了smalltalk语言。大多数语言中,classes仅仅是用于描述怎样创建一个对象的代码端。在某种程度上说,python中的class也是这样的。
    2016-02-02
  • python实现0到1之间的随机数方式

    python实现0到1之间的随机数方式

    这篇文章主要介绍了python实现0到1之间的随机数方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Python进行数据提取的方法总结

    Python进行数据提取的方法总结

    数据提取是分析师日常工作中经常遇到的需求。如某个用户的贷款金额,某个月或季度的利息总收入,某个特定时间段的贷款金额和笔数,大于5000元的贷款数量等等。本篇文章介绍如何通过python按特定的维度或条件对数据进行提取,完成数据提取需求。
    2016-08-08
  • PyMySQL数据库连接与优化方式

    PyMySQL数据库连接与优化方式

    本文介绍PyMySQL连接MySQL数据库的方法,涵盖CRUD操作、事务处理及连接池优化,通过合理配置连接池和错误重试机制,提升性能并确保数据一致性
    2025-10-10
  • Django在admin后台集成TinyMCE富文本编辑器的例子

    Django在admin后台集成TinyMCE富文本编辑器的例子

    今天小编就为大家分享一篇Django在admin后台集成TinyMCE富文本编辑器的例子,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-08-08
  • Python实现随机爬山算法

    Python实现随机爬山算法

    这篇文章主要介绍了Python实现随机爬山算法的方法,帮助大家更好的利用python进行机器学习,感兴趣的朋友可以了解下
    2021-01-01
  • 详解python pycharm与cmd中制表符不一样

    详解python pycharm与cmd中制表符不一样

    本文主要介绍了python pycharm与cmd中制表符不一样,这个问题通常是因为PyCharm和命令行(CMD)使用的制表符(tab)的宽度不同导致的,下面就来介绍一下解决方法,感兴趣的可以了解一下
    2025-08-08

最新评论