You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python Asyncio疑问:为何需3次await才能完成任务?

Asyncio协程执行时机疑问解答

问题场景

存在一个仅执行0.1秒sleep的协程,main函数为该协程创建任务,通过await asyncio.sleep(0)启动它,随后自身调用time.sleep(0.5)(时长大于协程sleep时间)。运行后出现两个疑问:

  1. 为何main函数唤醒后,协程仍未完成?
  2. 为何恰好需要3次await才能让协程完成?(测试多个协程时仍需3次)

测试代码

import asyncio
import time


async def coro():
    print('     coro started to sleep for 0.1 second')
    await asyncio.sleep(0.1)
    print('     [x] coro finished')


async def main():
    coro_task = asyncio.create_task(coro())
    await asyncio.sleep(0)
    time.sleep(0.5)
    i = 0
    while not coro_task.done():
        i += 1
        print(i)
        await asyncio.sleep(0)

asyncio.run(main())

运行输出

coro started to sleep for 0.1 second
1
2
3
     [x] coro finished

补充说明:若将time.sleep设为0.1,协程完成前会有数千次循环;time.sleep时长越长,循环次数越少;时长超过协程sleep时间后,循环次数固定为3。


疑问解答

疑问1:为何main函数唤醒后,协程仍未完成?

这是因为time.sleep(0.5)是阻塞式的同步调用,会直接占用当前线程,完全卡死asyncio的事件循环。

当你调用await asyncio.sleep(0)时,事件循环切换到coro执行,coro遇到await asyncio.sleep(0.1)后挂起,告诉事件循环“0.1秒后唤醒我”。但事件循环还没来得及处理这个定时器任务,main就调用了time.sleep(0.5)——这会让整个线程停摆,事件循环根本无法运行,自然没法检查coro的定时器是否到期,更没法唤醒coro继续执行。

等time.sleep(0.5)结束,main回到事件循环时,coro的定时器其实早就到期了,但事件循环必须等到下一次调度点(也就是你调用await asyncio.sleep(0)时)才会处理到期任务,所以此时coro仍处于未完成状态。

疑问2:为何恰好需要3次await才能让协程完成?

这和asyncio事件循环的调度逻辑以及定时器处理流程直接相关:

  1. 第一次await asyncio.sleep(0):事件循环从main切出,开始处理到期的定时器任务,但仅把coro的唤醒请求加入待处理队列,不会立即执行coro剩余代码。
  2. 第二次await asyncio.sleep(0):事件循环处理队列任务,将coro标记为可运行,但仍需等到下一次调度才能让coro获得执行权。
  3. 第三次await asyncio.sleep(0):coro终于拿到执行权,完成剩余代码、打印结束信息,任务被标记为done。

当time.sleep时长超过coro的sleep时间后,coro的定时器早已到期,事件循环需要走完“发现到期任务→标记可运行→执行任务”这三次调度流程,所以固定需要3次await。

而当time.sleep时长较短(比如0.1秒)时,coro的定时器可能还没到期,事件循环每次调度都要检查定时器状态,直到定时器真正到期后才会进入上述三次流程,因此循环次数会非常多。


内容的提问来源于stack exchange,提问作者Igor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 04:32:18