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

代码逻辑:

  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循环在周期性的运行并执行一些任务,所有任务执行完成会关闭循环。

伪代码示例如下:

任务列表 = [ 任务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()实现协程并发。

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)

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

python异步编程之asyncio初识的更多相关文章

  1. python异步编程之asyncio

    python异步编程之asyncio   前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率, ...

  2. python异步编程之asyncio(百万并发)

      前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率,弥补了python性能方面的短板,如最 ...

  3. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

  4. 异步编程之asyncio简单介绍

    引言: python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率,弥补了python性能方面的短板. as ...

  5. Python核心技术与实战——十八|Python并发编程之Asyncio

    我们在上一章学习了Python并发编程的一种实现方法——多线程.今天,我们趁热打铁,看看Python并发编程的另一种实现方式——Asyncio.和前面协程的那章不太一样,这节课我们更加注重原理的理解. ...

  6. python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...

  7. python并发编程之multiprocessing进程(二)

    python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...

  8. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  9. python并发编程之threading线程(一)

    进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...

  10. Python 多进程编程之multiprocessing--Pool

    Python 多进程编程之multiprocessing--Pool ----当需要创建的子进程数量不多的时候,可以直接利用multiprocessing 中的Process 动态生成多个进程, -- ...

随机推荐

  1. Salesforce LWC学习(四十五) lwc支持Console App控制Tab了

    本篇参考:https://help.salesforce.com/s/articleView?id=release-notes.rn_lwc_workspaceAPI.htm&release= ...

  2. Hugging News #0912: Hugging Face 2 人入选时代周刊全球百大 AI 人物

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...

  3. zxy 简单 dp 大讲堂

    讲课讲得非常清楚啊,我绝赞膜拜.节奏可以,思路清晰,解法自然,为讲师点赞. 第一个题是 loj3282 / joisc2020 - Treatment Project.原问题由 \(\left(S, ...

  4. 小札 Combinatorics & Inclusion-Exclusion Principle 1

    「codeforces - 340E」Iahub and Permutations link. 把 \(1,\dots,n\) 中剩下没被固定的数的数量记作 \(s\),再把这其中不担心有会填到自己身 ...

  5. Teamcenter RAC 开发之《日期控件》

    背景 Teamcenter Rac 开发客制化表单过程中,发现一个问题,就是使用日期控件JXDatePicker展示无法实现"签出"禁用控件输入 解决办法 选择使用JDateCho ...

  6. Jenkins 命令执行 -- jetty 敏感信息泄露 --(CVE-2021-2816)&&(CVE-2017-1000353)&&(CVE-2018-1000861)

    Jenkins 命令执行 -- jetty 敏感信息泄露 --(CVE-2021-2816)&&(CVE-2017-1000353)&&(CVE-2018-100086 ...

  7. 8.2 BeingDebugged

    BeingDebugged 是Windows系统PEB结构体中的一个成员,它是一个标志位,用于标识当前进程是否正在被调试.BeingDebugged的值为0表示当前进程未被调试,值为1表示当前进程正在 ...

  8. MySQL到TiDB:Hive Metastore横向扩展之路

    作者:vivo 互联网大数据团队 - Wang Zhiwen 本文介绍了vivo在大数据元数据服务横向扩展道路上的探索历程,由实际面临的问题出发,对当前主流的横向扩展方案进行了调研及对比测试,通过多方 ...

  9. 软件开发人员 Kubernetes 入门指南|Part 2

    在第 1 部分中,我们讲解了 Kubernetes 的核心组件,Kubernetes 是一种开源容器编排器,用于在分布式环境中部署和扩展应用程序:我们还讲解了如何在集群中部署一个简单的应用程序,然后更 ...

  10. 随身wifi 救砖过程记录

    7,8块钱买了个随身wifi,准备刷机玩的,后来不知道刷错了boot还是啥,加电后灯都不亮了,前期没备份,于是网上找了各种教程,下面记录下: 变砖后有个底层的9008驱动协议可以刷机,下面的过程都是基 ...