Python协程asyncio异步编程笔记分享

作者:季布, 时间:2022-03-05 22:23:31 

目录
  • 1.事件循环

  • 2.协程和异步编程

    • 2.1 基本使用

    • 2.2 await

    • 2.3 Task对象

1.事件循环

可以理解成为一个死循环,去检查任务列表中的任务,如果可执行就去执行,如果检查不到就是不可执行的,那就忽略掉去执行其他可执行的任务,如果IO结束了(比如说去百度下载图片,下载完了就会变成可执行任务)再去执行下载完成之后的逻辑


#这里的任务是有状态的,比如这个任务已经完成或者正在执行或者正在IO等待
任务列表 = [ 任务1, 任务2, 任务3,... ]
while True:
   可执行的任务列表,已完成的任务列表 = 去任务列表中检查所有的任务,将'可执行'和'已完成'的任务返回
   for 就绪任务 in 可执行的任务列表:
       执行已就绪的任务
   for 已完成的任务 in 已完成的任务列表:
       在任务列表中移除 已完成的任务
   如果 任务列表 中的任务都已完成,则终止循环

#在编写程序时候可以通过如下代码来获取和创建事件循环。
import asyncio
loop = asyncio.get_event_loop()
#将任务放到任务列表,让事件循环去检测任务的状态(是否可运行,是否IO)
loop.loop.run_until_complete(任务)

2.协程和异步编程

协程函数,定义形式为 async def 的函数。
协程对象,调用 协程函数() 所返回的对象。


# 定义一个协程函数
async def func():
   pass
# 调用协程函数,返回一个协程对象(内部代码不会执行)
result = func()

2.1 基本使用

程序中,如果想要执行协程函数的内部代码,需要 事件循环 和 协程对象 配合才能实现
示例1:


import asyncio
async def func():
   print("协程内部代码")
# 调用协程函数,返回一个协程对象。
result = func()
# 方式一
# loop = asyncio.get_event_loop() # 创建一个事件循环
# loop.run_until_complete(result) # 将协程当做任务提交到事件循环的任务列表中,协程执行完成之后终止。
# 方式二
# 本质上方式一是一样的,内部先 创建事件循环 然后执行 run_until_complete,一个简便的写法。
# asyncio.run 函数在 Python 3.7 中加入 asyncio 模块,
asyncio.run(result)

2.2 await

await+可等待对象(协程对象,Future,Task对象)
await是一个只能在协程函数中使用的关键字,用于遇到IO操作时挂起 当前协程(任务),当前协程(任务)挂起过程中 事件循环可以去执行其他的协程(任务),当前协程IO处理完成时,可以再次切换回来执行await之后的代码。


import asyncio
async def func():
   print("执行协程函数内部代码")
   # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。
   # 当前协程挂起时,事件循环可以去执行其他协程(任务)。
#response是IO耗时结束后拿到的结果
   response = await asyncio.sleep(2)
   print("IO请求结束,结果为:", response)
result = func()
asyncio.run(result)

结果
执行协程函数内部代码
IO请求结束,结果为: None
#这里返回None表示这个好事没有啥意义,如果你是下载了一张图片成功后会返回一个结果

示例2:


import asyncio
async def others():
   print("start")
   await asyncio.sleep(2)
   print('end')
   return '返回值'
async def func():
   print("执行协程函数内部代码")
   # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
   response = await others()
   print("IO请求结束,结果为:", response)
asyncio.run( func() )

执行结果:
执行协程函数内部代码
start
end
IO请求结束,结果为: 返回值

示例3:


import asyncio
async def others():
   print("start")
   await asyncio.sleep(2)
   print('end')
   return '返回值'
async def func():
   print("执行协程函数内部代码")
   # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
   #await等待有返回值才会向下执行
   response1 = await others()
   print("IO请求结束,结果为:", response1)
   response2 = await others()
   print("IO请求结束,结果为:", response2)
asyncio.run( func() )

执行结果:
执行协程函数内部代码
start
end
IO请求结束,结果为: 返回值
start
end
IO请求结束,结果为: 返回值

下一步依赖上一步的结果时使用await,但如果有其他任务依然会切换到其他任务去执行

2.3 Task对象

在事件循环中添加多个任务。
Tasks用于并发调度协程,通过asyncio.create_task(协程对象)的方式创建Task对象,这样可以让协程加入事件循环中等待被调度执行。

除了使用 asyncio.create_task() 函数以外,还可以用低层级的 loop.create_task() 或 ensure_future() 函数。

不建议手动实例化 Task 对象。

本质上是将协程对象封装成task对象,并将协程立即加入事件循环,同时追踪协程的状态。
注意:asyncio.create_task() 函数在 Python 3.7 中被加入。

在 Python 3.7 之前,可以改用低层级的 asyncio.ensure_future() 函数。

示例1:


import asyncio
async def func():
   print(1)
   await asyncio.sleep(2)
   print(2)
   return "返回值"
async def main():
   print("main开始")
   # 创建协程,将协程封装到一个Task对象中并立即添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
   task1 = asyncio.create_task(func())
   # 创建协程,将协程封装到一个Task对象中并立即添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
   task2 = asyncio.create_task(func())
   print("main结束")
   # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
   # 此处的await是等待相对应的协程全都执行完毕并获取结果
   ret1 = await task1
   ret2 = await task2
   print(ret1, ret2)
asyncio.run(main())

执行结果:
main开始
main结束
1
1
2
2
返回值 返回值

实例2:


import asyncio
async def func():
   print(1)
   await asyncio.sleep(2)
   print(2)
   return "返回值"
async def main():
   print("main开始")
   # 创建协程,将协程封装到Task对象中并添加到事件循环的任务列表中,等待事件循环去执行(默认是就绪状态)。
   # 在调用
   task_list = [
       asyncio.create_task(func(), name="n1"),
       asyncio.create_task(func(), name="n2")
   ]
   print("main结束")
   # 当执行某协程遇到IO操作时,会自动化切换执行其他任务。
   # 此处的await是等待所有协程执行完毕,并将所有协程的返回值保存到done
   # 如果设置了timeout值,则意味着此处最多等待的秒,完成的协程返回值写入到done中,未完成则写到pending中(比如下面的Timeout=1,要下载的图片是两秒,设为1秒就会执行失败,done内为空,而pending中就是执行失败的)。
   done, pending = await asyncio.wait(task_list, timeout=None)
   print(done, pending)
asyncio.run(main())

执行结果:
main开始
main结束
1
1
2
2
{
  <Task finished name='n1' coro=<func() done, defined at C:/Users/xuan.li/Desktop/unidevopss/py3x64/ibuildmaster/apps/PROD/tests.py:513> result='返回值'>,
  <Task finished name='n2' coro=<func() done, defined at C:/Users/xuan.li/Desktop/unidevopss/py3x64/ibuildmaster/apps/PROD/tests.py:513> result='返回值'>
}
set()

示例3:


import asyncio
async def func():
   print("执行协程函数内部代码")
   # 遇到IO操作挂起当前协程(任务),等IO操作完成之后再继续往下执行。当前协程挂起时,事件循环可以去执行其他协程(任务)。
   response = await asyncio.sleep(2)
   print("IO请求结束,结果为:", response)
coroutine_list = [func(), func()]
# 错误:coroutine_list = [ asyncio.create_task(func()), asyncio.create_task(func()) ]  
# 此处不能直接 asyncio.create_task,因为将Task立即加入到事件循环的任务列表,
# 但此时事件循环还未创建,所以会报错。
# 使用asyncio.wait将列表封装为一个协程,并调用asyncio.run实现执行两个协程
# asyncio.wait内部会对列表中的每个协程执行ensure_future,封装为Task对象。
done,pending = asyncio.run( asyncio.wait(coroutine_list) )

来源:https://blog.csdn.net/weixin_47906106/article/details/120249639

标签:Python,协程,asyncio,异步编程
0
投稿

猜你喜欢

  • 微信跳一跳辅助python代码实现

    2023-03-26 15:25:50
  • Django框架中方法的访问和查找

    2022-07-23 16:31:35
  • 如何检测用户第一次访问我的网站并显示友好信息?

    2009-11-25 20:33:00
  • css学习笔记:为什么在IE6下图片会往下掉一行

    2009-06-19 12:50:00
  • Oracle与SQL Server数据库镜像对比

    2009-03-25 14:27:00
  • form的submit方法和submit事件(onsubmit)

    2008-09-28 13:29:00
  • 编码问题引起的折腾

    2009-07-03 12:43:00
  • Django web框架使用url path name详解

    2022-01-06 19:43:59
  • 用ASP实现分级权限控制

    2008-10-09 13:02:00
  • 如何从MySQL数据库表中检索数据

    2008-11-01 17:08:00
  • Python读取yaml文件的详细教程

    2021-03-16 20:43:27
  • PHP递归调用数组值并用其执行指定函数的方法

    2023-09-05 15:35:04
  • MYSQL中取得最后一条记录

    2010-03-09 16:08:00
  • web标准页面知识必备 Ⅰ

    2008-03-06 13:24:00
  • Go的固定时长定时器和周期性时长定时器

    2023-06-27 12:44:23
  • 5步让你的CSS样式表成功减肥

    2009-08-02 21:27:00
  • PHP实现表单处理方法详解

    2023-05-25 07:39:18
  • 显示某日期所在星期的所有日期asp代码

    2008-06-01 16:05:00
  • ASP编程中的常见问题

    2007-09-20 13:32:00
  • 用JMail、CDONTS发送邮件asp源码

    2007-09-24 15:58:00
  • asp之家 网络编程 m.aspxhome.com