1,背景:
比较好奇异步生成器是如何执行的,协程如何启动,什么时候挂起,调度协程的事件循环在什么节点启动。
2,观察协程执行过程
代码如下:总共有一个主协程+3个task协程,整体过程如下:
- Step1:首先通过asyncio.run启动事件循环(该事件循环用于协调所有协程的运行),运行一个协程(该协程执行下面main_process的处理,这里我们暂时叫它主协程)
- Step2:主协程里面通过asyncio.create_task生成了三个新的协程,task1,2,3。所以目前总共有主协程+task1,2,3,总共4个协程。
- Step3:事件循环会在这四个协程中进行调度。从这4个中,选取一个准备完毕可以执行的协程,运行该协程,直到协程挂起(下面的await处理)。协程挂起之后,事件循环会将继续选取另外一个准备完毕的协程,继续执行。
# 定义一个异步的生成器
async def generator(name):
for i in range(3):
print(f"【{name}】-开始异步操作: {i},协程挂起2s")
await asyncio.sleep(3) # 这里模拟异步耗时操作(比如文件io,网络读取等),协程挂起3s
print(f"【{name}】-异步操作完成,生成: {i}")
yield i
# 定义一个异步函数来处理生成器
async def process_generator(name):
# 进入协程处理
print(f"【{name}】进入协程处理:")
# 如果是普通的for循环,是同步执行,会在协程中全部执行完毕,再进入到后续处理
# for item in range(0,2):
# print(f'【{name}】普通的生成器:{item}')
# async for:异步迭代的语法,它允许你以异步的方式遍历异步迭代器。
async for item in generator(name):
print(f"【{name}】协程处理继续: 生成{item}")
print(f"【{name}】协程处理继续: 即将进入下一个生成")
# 主函数
async def main_process():
print('【主协程】进入main处理')
print('【主协程】生成 协程 task1')
task1 = asyncio.create_task(process_generator('task1')) #生成一个新的协程
print('【主协程】生成 协程 task2')
task2 = asyncio.create_task(process_generator('task2')) #生成一个新的协程
print('【主协程】生成 协程 task3')
task3 = asyncio.create_task(process_generator('task3')) #生成一个新的协程
# 获取当前事件循环中的所有任务
tasks = asyncio.all_tasks()
print(f"【主协程】当前事件循环中有 {len(tasks)} 个任务(一个主协程+3个task协程)")
# 等待所有任务完成
print(f"【主协程】挂起,等待task1完成")
await task1
print(f"【主协程】挂起,等待task2完成")
await task2
print(f"【主协程】挂起,等待task3完成")
await task3
print('【主协程】全部完成')
# 启动事件循环(该事件循环用于协调所有协程的运行),运行一个协程,执行main_process函数
asyncio.run(main_process())
执行结果:
【主协程】进入main处理
【主协程】生成 协程 task1
【主协程】生成 协程 task2
【主协程】生成 协程 task3
【主协程】当前事件循环中有 4 个任务(一个主协程+3个task协程)
【主协程】挂起,等待task1完成
【task1】进入协程处理:
【task1】-开始异步操作: 0,协程挂起2s
【task2】进入协程处理:
【task2】-开始异步操作: 0,协程挂起2s
【task3】进入协程处理:
【task3】-开始异步操作: 0,协程挂起2s
【task1】-异步操作完成,生成: 0
【task1】协程处理继续: 生成0
【task1】协程处理继续: 即将进入下一个生成
【task1】-开始异步操作: 1,协程挂起2s
【task2】-异步操作完成,生成: 0
【task2】协程处理继续: 生成0
【task2】协程处理继续: 即将进入下一个生成
【task2】-开始异步操作: 1,协程挂起2s
【task3】-异步操作完成,生成: 0
【task3】协程处理继续: 生成0
【task3】协程处理继续: 即将进入下一个生成
【task3】-开始异步操作: 1,协程挂起2s
【task1】-异步操作完成,生成: 1
【task1】协程处理继续: 生成1
【task1】协程处理继续: 即将进入下一个生成
【task1】-开始异步操作: 2,协程挂起2s
【task2】-异步操作完成,生成: 1
【task2】协程处理继续: 生成1
【task2】协程处理继续: 即将进入下一个生成
【task2】-开始异步操作: 2,协程挂起2s
【task3】-异步操作完成,生成: 1
【task3】协程处理继续: 生成1
【task3】协程处理继续: 即将进入下一个生成
【task3】-开始异步操作: 2,协程挂起2s
【task1】-异步操作完成,生成: 2
【task1】协程处理继续: 生成2
【task1】协程处理继续: 即将进入下一个生成
【task2】-异步操作完成,生成: 2
【task2】协程处理继续: 生成2
【task2】协程处理继续: 即将进入下一个生成
【task3】-异步操作完成,生成: 2
【task3】协程处理继续: 生成2
【task3】协程处理继续: 即将进入下一个生成
【主协程】挂起,等待task2完成
【主协程】挂起,等待task3完成
【主协程】全部完成Process finished with exit code 0