asyncio.gather默认遇异常即取消其余协程,加return_exceptions=True可让异常作为返回值;它与create_task+wait的区别在于:gather按参数顺序返回全部结果,而后者支持按完成顺序处理、超时控制和单独管理任务。

asyncio.gather 会等待所有协程完成,但不会自动处理异常
默认情况下,asyncio.gather 只要有一个协程抛出未捕获异常,整个调用就立即失败,其余协程会被取消(即使它们已开始运行)。这和你“并行执行多个任务”的直觉可能冲突——你以为它们各自独立,其实不是。
常见错误现象:asyncio.CancelledError 大量出现,或只看到第一个异常,后续协程状态不明。
- 加
return_exceptions=True参数,让异常也作为返回值(类型为Exception实例),避免中断:
import asyncio
<p>async def fetch_user(user_id):
await asyncio.sleep(0.1)
if user_id == 2:
raise ValueError("user not found")
return {"id": user<em>id, "name": f"user</em>{user_id}"}</p><p>async def main():
results = await asyncio.gather(
fetch_user(1),
fetch_user(2),
fetch_user(3),
return_exceptions=True
)</p><h1>results: [dict, ValueError, dict]</h1><pre class="brush:php;toolbar:false;">for r in results:
if isinstance(r, Exception):
print(f"failed: {r}")
else:
print(f"ok: {r}")asyncio.run(main())
asyncio.gather 和 asyncio.create_task + asyncio.wait 的区别在哪
asyncio.gather 是高层封装,适合“等全部结果、统一返回”的场景;而 asyncio.create_task 配合 asyncio.wait 或 asyncio.as_completed 更适合需要“按完成顺序处理”或“超时控制”“部分失败容忍”的情况。
-
gather:返回值顺序严格对应参数顺序,不支持超时(需外层套asyncio.wait_for) -
as_completed:谁先完成谁先 yield,适合流式处理或快速失败判断 -
create_task后手动await task:可单独取消、检查状态、重试
例如,你想在 2 秒内尽可能拿到结果,不管顺序:
tasks = [asyncio.create_task(fetch_user(i)) for i in range(1, 4)]
done, pending = await asyncio.wait(tasks, timeout=2.0)
for t in done:
print(await t) # 注意:t 是 Task,需 await 获取结果
传给 gather 的协程不能是已 await 过的
常见错误:把 await some_coro() 的结果(即普通值或异常)传进 gather,导致 TypeError: an integer is required (got type int) 或类似报错。
正确做法:只传协程对象(函数调用但不加 await):
- ✅ 正确:
gather(coroutine_a(), coroutine_b()) - ❌ 错误:
gather(await coroutine_a(), await coroutine_b())—— 这已经不是协程了 - ⚠️ 特别注意:如果协程来自变量,确认它没被提前调用过,比如
coro = fetch_user(1)是 OK 的,但coro = await fetch_user(1)就不行
大量协程并发时,记得限制并发数
asyncio.gather 不做并发节流。1000 个协程一起发出去,可能瞬间打爆网络连接池、触发服务端限流、或耗尽本地文件描述符。
解决方式不是不用 gather,而是分批调用:
- 用
asyncio.Semaphore包裹每个协程调用 - 或手动切片,每批最多 N 个,用
gather批量执行
简单节流示例:
sem = asyncio.Semaphore(10) # 最多 10 个并发 <p>async def limited_fetch(user_id): async with sem: return await fetch_user(user_id)</p><h1>然后 gather(limited_fetch(1), limited_fetch(2), ...)</h1><p></p>
真正容易被忽略的是:协程本身是否包含阻塞调用(如 time.sleep、同步 HTTP 库),那即便用了 gather,也会卡住整个事件循环——必须确保所有 I/O 都是异步的。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











