贝利信息

asyncio.timeout() 在 3.11+ 如何与 gather/wait 配合使用

日期:2026-01-25 00:00 / 作者:冰川箭仙
asyncio.timeout() 是上下文管理器而非超时参数,须用 async with 包裹 gather() 或 wait();超时触发时自动取消未完成任务,但要求协程支持取消,否则无法强制终止。

asyncio.timeout() 不能直接传给 gather() 或 wait()

它不是超时参数,而是上下文管理器或异步生成器。常见错误是写成 asyncio.gather(..., timeout=asyncio.timeout(5)) —— 这会报 TypeError: gather() got an unexpected keyword argument 'timeout'。正确做法是把 gather()wait() 整个包进 asyncio.timeout() 的作用域里。

用 async with asyncio.timeout() 包裹 gather()

这是最直观、推荐的组合方式。超时触发时,所有正在运行的子协程会被取消(前提是它们能响应取消):

async def fetch_user(user_id):
    await asyncio.sleep(2)
    return f"user_{user_id}"

async def main(): try: async with asyncio.timeout(3): results = await asyncio.gather( fetch_user(1), fetch_user(2), fetch_user(3) ) print(results) except asyncio.TimeoutError: print("至少一个任务超时了,整个 gather 被中断")

wait() 配合 timeout() 时要注意 done/pending 分离

asyncio.wait() 返回 (done, pending),而 asyncio.timeout() 不改变这个行为。你得手动检查 pending 是否非空:

tasks = [asyncio.create_task(fetch_user(i)) for i in range(3)]
try:
    async with asyncio.timeout(2.5):
        done, pending = await asyncio.wait(tasks,

return_when=asyncio.FIRST_COMPLETED) # 此时 pending 可能还有未完成的任务 for t in pending: t.cancel() except asyncio.TimeoutError: # 所有剩余 task 已被 cancel,但需 await 它们以清理状态 await asyncio.gather(*pending, return_exceptions=True)

嵌套 timeout() 或与 shield() 冲突要小心

如果你在子协程里也用了 asyncio.timeout(),外层超时会优先触发并取消内层;而 asyncio.shield() 会让被保护的协程忽略外层取消 —— 这可能导致 gather() 卡住或超时不生效:

真正难处理的是那些不响应取消的协程(比如调用了阻塞的 time.sleep() 或没加 await 的 CPU 密集循环),这种情况下 timeout() 无法强制终止,只能等它自己结束。