python 协程之asyncio(一)
介绍
asyncio是用来编写并发代码的库,使用async/await语法。
asyncio被用作与多个提供高性能python异步框架的基础,包括网络和网站服务,数据库连接库,分布式任务队列等等
在实际的开发中,为了实现更高的并发有很多的方案,比如多进程、多线程。但是无论是多进程还是多线程,IO的调度更多的取决于操作系统,而协程的方式,其调度确是来自于用户,用户在函数中yield
一个状态。使用协程可以实现高效的并发任务。
最简单的示例
import asyncio async def say(name): print(name,'hello','开始') await asyncio.sleep(2) print(name,'hello','结束') if __name__ == '__main__': loop=asyncio.get_event_loop() loop.run_until_complete(say('jack'))
基本流程
-
通过关键字
async
定义一个协程对象 - 通过await 将尚未实现完毕的程序挂起
-
协程不能直接运行,所以要丢进事件循环
loop
,由loop
在适当的时候调用 -
asycio.get_event_loop
创建一个事件循环 -
run_until_complete
注册协程到事件循环并启动
创建任务
协程对象在注册到循环事件的时候,也就是在调用run_until_complete
之后将协程对象打包成一个任务对象。所谓的任务对象其本质就是一个Future
类的子类。它会保存运行后的状态,用于获取该协程执行的结果。
介绍一下常用的方法:
-
event_loop
:事件循环。开启一个事件循环,只需要将函数注册到事件循环,在条件满足的时候调用 -
coroutione
:协程对象,使用关键字async
声明的函数不会立即执行,而是返回一个协程对象。协程对象就是原生可以挂起的函数 -
task
:任务对象。将协程对象进一步封装,就变成了任务,它包含各种任务的状态 -
future
:任务结果。不管是将来执行还是没有执行的任务,它都代表这个任务的结果。和task
并没有本质上的区别 -
async/await
:关键字。前者用于定义一个协程,后者用于挂起阻塞的异步调用
import asyncio,time async def say(name): print(name,'hello','开始') await asyncio.sleep(2) print(name,'hello','结束') now=lambda:time.time() start=now() if __name__ == '__main__': #创建协程对象 result=say('jack') #创建事件循环 loop=asyncio.get_event_loop() #创建任务对象,生成任务包 task=loop.create_task(result) print('task:',task) #注册协程对象到事件循环并执行 loop.run_until_complete(task) print('task:', task) print('耗时:%0.2f' % (now() - start),'s')
可以看到,在get_event_loop
之后,在加入事件循环之前处于pending
状态,在run_until_complete
之后,其状态变成了finished
。
上边的代码task还可以通过asyncio.ensure_future(coroutine)
来创建,run_until_complete
参数就是future
对象,在传入协程之后封装成task,而task是future的子类,可以使用inistance
函数检验
创建协程对象如果用gather
的话,后边await
的返回值就是协程对象的执行结果:
修改下代码即可:
result2=loop.run_until_complete(asyncio.gather(task))
print('result2:',result2)
获取执行结果
获取协程对象的执行结果有两种方法,一种是通过回调获取,一种是直接result。
绑定回调
在task执行完毕后可以获取结果,回调的最后一个参数为future
对象,可以通过这个对象来获取协程的返回值,这也就是协程里面常说的绑定回调
import asyncio,time
async def say(name):
print(name,'hello','开始')
await asyncio.sleep(2)
print(name,'hello','结束')
return '%s 已执行完毕' % name
def callback(future):
print('callback:', future.result())
now=lambda:time.time()
start=now()
if __name__ == '__main__':
#创建协程对象
result=say('jack')
#创建事件循环
loop=asyncio.get_event_loop()
#创建任务对象,生成任务包
task=loop.create_task(result)
# 回调函数
task.add_done_callback(callback)
#注册协程对象到事件循环并执行
loop.run_until_complete(task)
print(task)
print('耗时:%0.2f' % (now() - start),'s')
但是如果回调需要多个参数的话怎么办?学过python基础的都知道,偏函数正好能解决该类问题。将future
作为固定参数,极大的减少了编程成本,也非常好的遵循了DRY原则。
假设上述代码中的callback函数需要再传入一个时间参数,就可以这么做
from functools import partial import asyncio,time async def say(name): print(name,'hello','开始') await asyncio.sleep(2) print(name,'hello','结束') return '%s 已执行完毕' % name def callback(now,future): print('callback:%s,当前时间:%s'% (future.result(),now)) now=lambda:time.time() start=now() if __name__ == '__main__': #创建协程对象 result=say('jack') #创建事件循环 loop=asyncio.get_event_loop() #创建任务对象,生成任务包 task=loop.create_task(result) # 回调函数 task.add_done_callback(partial(callback,time.ctime())) #注册协程对象到事件循环并执行 loop.run_until_complete(task) print('耗时:%0.2f' % (now() - start),'s')
直接获取
将task
调用result
方法即可
task.result()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程