asyncio事件循环任务调度机制及测试程序输出异常问询
未await的asyncio任务为何会执行部分代码?
核心原因:任务创建后的立即预执行
用asyncio.create_task()包装协程时,asyncio会立刻启动该协程的执行,直到它碰到第一个await(或yield from)。这就是你能看到start a和start b输出的原因——这两行代码是在协程遇到await asyncio.sleep(0)之前同步跑完的。
纠正await与yield的理解偏差
你之前认为任务只在遇到yield时交还控制权,这点本身没错,但要明确:
await是yield from的语法糖,最终都会触发协程暂停并把控制权交还给事件循环。- 但在
await之前的代码,会被同步执行,不会触发调度——直到碰到第一个可等待对象,协程才会暂停,让事件循环去处理其他任务。
你的测试代码逐行拆解
看你的a()协程:
async def a(): print(f"start a {datetime.now()}") # 任务创建后立即执行这行 await asyncio.sleep(0) # 碰到第一个await,协程暂停,交还控制权 print(f"done a {datetime.now()}") # 这行没机会执行,因为事件循环后续直接关闭了
执行流程的关键节点:
- 调用
asyncio.run(main())启动事件循环,运行main()协程。 main()里创建任务a和b,这两个任务被加入事件循环的任务队列,立刻开始执行,直到各自的await点暂停。main()协程没有await任何任务,执行完毕后,事件循环进入清理阶段:它会尝试运行所有已提交的任务,但任务a和b已经暂停在await asyncio.sleep(0)处,此时事件循环已经准备关闭,不会再调度这些任务继续执行,所以done a和done b不会输出。- 事件循环关闭后,回到主线程,打印
back to main。
为什么你的预期流程不成立?
你误以为创建任务后不会执行,但asyncio.create_task()的设计就是立即调度任务启动,而不是把任务放在队列里等后续主动触发。这种预执行特性是为了让任务尽快开始处理逻辑。
验证实验:去掉await会怎样?
如果把a()和b()中的await asyncio.sleep(0)删掉,这两个任务会在创建后直接执行完毕,你会看到done a和done b也被打印出来——因为协程没有暂停点,会一次性跑完所有代码。
关键结论
asyncio.create_task()创建任务后,事件循环会立即执行该协程,直到第一个await/yield from出现。asyncio.run()在主协程结束后,会处理已提交的任务到暂停点,之后才关闭事件循环。- 你对yield和await的底层关系理解正确,但忽略了任务创建后的立即预执行行为。
内容的提问来源于stack exchange,提问作者tuannamnguyen290602
相关产品推荐
相关产品推荐

