python异步编程之asyncio高阶API

asyncio 高阶API列表
asyncio中函数可以分为高阶函数和低阶函数。低阶函数用于调用事件循环、linux 套接字、信号等更底层的功能,高阶函数是屏蔽了更多底层细节的任务并发,任务执行函数。通常开发中使用更多的是高阶函数。本篇主要介绍asyncio中常用的高阶函数。
由于asyncio在不同的版本中有差异,本文以及本系列都以python3.10为准。
| 函数 | 功能 |
|---|---|
| run() | 创建事件循环,运行一个协程,关闭事件循环。 |
| create_task() | 创建一个asyncio的Task对象 |
| await sleep() | 休眠几秒 |
| await gather() | 并发执行所有事件的调度和等待 |
| await wait_for() | 有超时控制的运行 |
| await shield() | 屏蔽取消操作 |
| await wait() | 完成情况的监控器 |
| current_task() | 返回当前Task对象 |
| all_tasks() | 返回事件循环中所有的task对象 |
| Task | Task对象 |
| to_thread() | 在不同的 OS 线程中异步地运行一个函数 |
| run_coroutine_threadsafe() | 从其他OS线程中调度一个协程 |
| for in as_completed() | 用 for 循环监控完成情况 |
run
函数原型:
asyncio.run(coro, *, debug=False)
功能:创建事件循环,运行传入的协程。该函数总是会创建一个新的事件循环并在结束时关闭它,应该被当做asyncio程序的主入口点。run() 函数是用来创建事件,将task加入事件,运行事件的函数。
async def main():
await asyncio.sleep(1)
print('hello')
asyncio.run(main())
run() 从功能上等价于以下低阶API。获取一个事件循环,创建一个task,加入事件循环。
loop = asyncio.get_event_loop()
task = loop.create_task(main())
loop.run_until_complete(task)
create_task
函数原型:
asyncio.create_task(coro, *, name=None)
功能:将协程函数封装成一个Task。协程函数没有生命周期,但是Task有生命周期。
将协程打包为一个 Task 并自动寻找事件循环加入。返回 Task 对象。该任务会在 get_running_loop() 返回的循环中执行,如果当前线程没有在运行的循环则会引发 RuntimeError。
async def coro():
await asyncio.sleep(1)
print("i am coro")
async def main():
task = asyncio.create_task(coro())
print(f"task状态:{task._state}")
await asyncio.sleep(2)
print(f"task状态:{task._state}")
print("i am main")
asyncio.run(main())
结果:
task状态:PENDING
i am coro
task状态:FINISHED
i am main
结果分析:
可以看到task运行中的状态和结束的生命周期状态
gather
函数原型:
asyncio.gather(*aws, return_exceptions=False)
功能:
并发执行所有可等待对象,收集任务结果,返回所有已经完成的task的结果。结果将是一个由所有返回值组成的列表。结果值的顺序与传入的task的顺序一致。可等待对象可以是协程和task。
如果序列中是协程而不是task,那么会将其自动封装成task加入事件循环。
import asyncio
async def coro(value):
print(f"hello coro{value}")
return f"coro{value}"
async def main():
tasks = [coro(i) for i in range(5)]
res = await asyncio.gather(*tasks)
for i in res:
print(i)
asyncio.run(main())
结果:
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
coro0
coro1
coro2
coro3
coro4
结果分析:
获取了所有协程的返回值,并且返回的顺序和任务的顺序一致。
wait
函数原型:
asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
功能:
并发地运行序列中的可等待对象,并进入阻塞状态直到满足 return_when 所指定的条件。将task任务结果收集起来,返回两个 Task/Future 集合: (done, pending)。done是已经完成的任务,pending是未完成的任务,未完成的原因可能是超时或return_when策略。
aws:
aws中保存的是task而不是协程,从3.8起不建议传入协程,3.11将不再支持传入协程。
timeout:
如指定 timeout (float 或 int 类型) 则它将被用于控制返回之前等待的最长秒数。
请注意此函数不会引发 asyncio.TimeoutError。当超时发生时,未完成的 Future 或 Task 将不会继续执行,不会返回结果。
return_when:
return_when 指定此函数应在何时返回。它必须为以下参数之一:
| 参数 | 描述 |
|---|---|
| FIRST_COMPLETED | 函数将在任意可等待对象结束或取消时返回。 |
| FIRST_EXCEPTION | 函数将在任意可等待对象因引发异常而结束时返回。当没有引发任何异常时它就相当于 ALL_COMPLETED。 |
| ALL_COMPLETED | 函数将在所有可等待对象结束或取消时返回。 |
基础使用示例:
import asyncio
async def coro(value):
print(f"hello coro{value}")
return f"coro{value}"
async def main():
tasks = [asyncio.create_task(coro(i)) for i in range(5)]
done, pending = await asyncio.wait(tasks)
for i in done:
print(i.result())
asyncio.run(main())
结果:
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
coro1
coro2
coro0
coro3
coro4
结果分析:
返回结果和执行顺序并不是一致的
指定超时时间:
import asyncio
from asyncio import FIRST_COMPLETED
async def coro(value):
print(f"hello coro{value}")
await asyncio.sleep(value)
return f"coro{value}"
async def main():
tasks = [asyncio.create_task(coro(i)) for i in range(5)]
done, pending = await asyncio.wait(tasks, timeout=3)
print("---------finish----------")
for i in done:
print(i.result())
print("---------pending----------")
for i in pending:
print(i)
asyncio.run(main())
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
---------finish----------
coro1
coro2
coro0
---------pending----------
<Task pending name='Task-5' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future finished result=None>>
<Task pending name='Task-6' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
结果分析:
超时未完成的task会保存在pending中,未完成的task在超时之后不会继续执行,没有返回结果。
return_when配置任意任务完成就返回:
import asyncio
from asyncio import FIRST_COMPLETED
async def coro(value):
print(f"hello coro{value}")
await asyncio.sleep(value)
return f"coro{value}"
async def main():
tasks = [asyncio.create_task(coro(i)) for i in range(5)]
done, pending = await asyncio.wait(tasks, return_when=FIRST_COMPLETED)
print("---------finish----------")
for i in done:
print(i.result())
print("---------pending----------")
for i in pending:
print(i)
asyncio.run(main())
结果:
hello coro0
hello coro1
hello coro2
hello coro3
hello coro4
---------finish----------
coro0
---------pending----------
<Task pending name='Task-5' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
<Task pending name='Task-3' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
<Task pending name='Task-4' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
<Task pending name='Task-6' coro=<coro() running at /Users/ljk/Documents/code/daily_dev/async_demo/wait_demo.py:6> wait_for=<Future pending cb=[Task.task_wakeup()]>>
结果分析:
获取到任意结果就返回,未完成的task保存在pending中。未完成的task在超时之后不会继续执行。
as_completed
函数原型:
asyncio.as_completed(aws, *, timeout=None)
说明:并发执行aws中保存的可等待对象,返回一个协程的迭代器。可以从迭代器中取出最先执行完成的task的结果。返回结果和执行顺序不一致。aws中可以是task或协程序列。
import asyncio
async def coro(value):
print(f"hello coro{value}")
return f"coro{value}"
async def main():
tasks = [coro(i) for i in range(5)]
for item in asyncio.as_completed(tasks):
res = await item
print(res)
asyncio.run(main())
结果:
hello coro2
hello coro3
hello coro4
hello coro1
hello coro0
coro2
coro3
coro4
coro1
coro0
结果分析:
所有任务都会执行完成,没有超时配置。返回顺序和执行顺序无关。
gather、wait、as_completed 异同点小结
asyncio协程体系中可以实现创建多个任务并发执行的函数有以下三个:
- asyncio.gather
- asyncio.wait
- asyncio.as_completed
不同之处比较:
| 特性/函数 | gather | wait | as_completed |
|---|---|---|---|
| 入参 | 同时支持task和协程序列 | 只支持task序列 | 同时支持task和协程序列 |
| 获取结果顺序 | 有序,和并发序列顺序相同 | 无序,和并发序列无关 | 无序,和并发序列无关 |
| 返回 | 返回结果列表,保存的是函数返回值。 | 返回元组done、pending。元组中保存的是task,而非task 的函数返回值 | 返回一个迭代器,从中可迭代出函数返回值。 |
wait for
函数原型:
asyncio.wait_for(aw, timeout)
功能:执行单个可等待对象,指定 timeout 秒数后超时
等待可等待对象完成,指定timeout秒数后超时。和gather类似,可以自动将协程转化成任务加入循环。
timeout 可以为 None,也可以为 float 或 int 型数值表示的等待秒数。如果 timeout 为 None,则等待直到完成。
如果发生超时,任务将取消并引发 asyncio.TimeoutError。
async def coro():
# 睡眠5s
await asyncio.sleep(3600)
print('finish!')
async def main():
# Wait for at most 1 second
try:
await asyncio.wait_for(coro(), timeout=1.0)
except asyncio.TimeoutError:
print('timeout!')
asyncio.run(main())
结果:
timeout!
高阶API中常用的函数基本就是这些,下一篇分析低阶函数。
连载一系列关于python异步编程的文章。包括同异步框架性能对比、异步事情驱动原理等。欢迎关注微信公众号第一时间接收推送的文章。

python异步编程之asyncio高阶API的更多相关文章
- python异步编程之asyncio
python异步编程之asyncio 前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率, ...
- python异步编程之asyncio(百万并发)
前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率,弥补了python性能方面的短板,如最 ...
- python并发编程之asyncio协程(三)
协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...
- Python核心技术与实战——十八|Python并发编程之Asyncio
我们在上一章学习了Python并发编程的一种实现方法——多线程.今天,我们趁热打铁,看看Python并发编程的另一种实现方式——Asyncio.和前面协程的那章不太一样,这节课我们更加注重原理的理解. ...
- 异步编程之asyncio简单介绍
引言: python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率,弥补了python性能方面的短板. as ...
- python并发编程之gevent协程(四)
协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...
- python并发编程之multiprocessing进程(二)
python的multiprocessing模块是用来创建多进程的,下面对multiprocessing总结一下使用记录. 系列文章 python并发编程之threading线程(一) python并 ...
- python并发编程之Queue线程、进程、协程通信(五)
单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...
- python并发编程之threading线程(一)
进程是系统进行资源分配最小单元,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.进程在执行过程中拥有独立的内存单元,而多个线程共享内存等资源. 系列文章 py ...
- 异步编程之co——源码分析
异步编程系列教程: (翻译)异步编程之Promise(1)--初见魅力 异步编程之Promise(2):探究原理 异步编程之Promise(3):拓展进阶 异步编程之Generator(1)--领略魅 ...
随机推荐
- 「luogu - P3911」最小公倍数之和
link. Denote \(cnt_{x}\) = the number of occurrences of \(x\), \(h\) = the maximum of \(a_i\), there ...
- Go语言系列——21-Go协程、22-信道(channel)、23-缓冲信道和工作池、24-Select、25-Mutex、26-结构体取代类、27-组合取代继承、多态、 29-Defer、错误处理
文章目录 21-Go协程 Go 协程是什么? Go 协程相比于线程的优势 如何启动一个 Go 协程? 启动多个 Go 协程 22-信道(channel) 什么是信道? 信道的声明 通过信道进行发送和接 ...
- IntelliJ IDEA安装中文插件
1.运行IntelliJ IDEA程序2.点击左上角"File"//文件3.点击下拉的"Settings" //设置4.点击"Plugins" ...
- Rockchip rk3588 U-Boot详解 (三)
Rockchip rk3588 U-Boot详解 (三) 专栏总目录 1.1 Environment-Variables ENV(Environment-Variables)是U-Boot支持的一种全 ...
- Cloud Bursting解决方案,Serverless容器降本增效极致体验
本文分享自华为云社区<DTSE Tech Talk | 第42期:Cloud Bursting解决方案,Serverless容器降本增效极致体验>,作者:华为云社区精选. 线下IDC自建K ...
- 彻底搞懂Docker容器与Kraft模式kafka集群关于消息大小相关参数设置
Docker部署的设置 部署背景: 在DockerHub拉取的bitnami/kafka:3.4.1 镜像,如果要部署在Docker-Swarm集群或者单Docker部署,对于消息大小设置需要添加参数 ...
- [ABC208E] Digit Products 题解
Digit Products 题目大意 求有多少个不大于 \(n\) 的正整数,使得该正整数各位乘积不大于 \(k\). 思路分析 观察数据范围,首先考虑数位 DP. 考虑设计记忆化搜索函数 dfs( ...
- Android下音视频对讲演示程序(声学回音消除、噪音抑制、语音活动检测、自动增益控制、自适应抖动缓冲)(2023年07月13日更新)
Android下音视频对讲演示程序 必读说明 简介 本软件根据<道德经>为核心思想而设计,实现了两个设备之间进行音视频对讲,一般可用于楼宇对讲.智能门铃对讲.企业员工对讲.智能对讲机. ...
- 关于STM32F407ZGT6的USB损坏后使用ST-Link和USART1实现串口功能
开发板:STM32F407ZGT6: 目标:想使用软件"串口调试助手" 情况:开发板上的USB_UART口所在器件损坏或者直接没有: 解决办法:查看该开发板的原理图,可得:串口1的 ...
- Keepalived高可用软件概述
Keepalived高可用软件概述: 1)互联网主要的高可用软件:Keepalived.Hearttbeat.其中Keepalived是轻量级的,Keepalived是一款开源.免费的实现网站.数据库 ...