asyncio.gather默认遇异常即中断所有任务,需显式设return_exceptions=true使异常作为结果返回以保障并发执行;须用isinstance(r, exception)手动判别成败,且不适用于依赖型任务。

asyncio.gather 会等待所有任务完成,但不会自动处理异常
默认情况下,asyncio.gather 遇到任意一个协程抛出未捕获异常,就会立即中断其余正在运行的任务,并把异常向上抛出。这和“并发执行、各自独立失败”的常见需求不符。
如果你希望某几个任务失败不影响其他任务继续执行,必须显式传入 return_exceptions=True 参数:
results = await asyncio.gather(
fetch_user(1),
fetch_user(2),
fetch_user(3),
return_exceptions=True
)
此时即使 fetch_user(2) 抛出 HTTPError,results 列表里对应位置也会是该异常实例,而不是中断整个调用。
- 不加
return_exceptions=True:一个失败,全部中断,报错信息直接是那个异常 - 加了之后:所有任务尽力完成,结果列表中混有返回值和异常对象,需手动检查
isinstance(r, Exception) - 注意:它不会“吞掉”异常——你仍需处理,只是不再让异常打断流程
并发数量不受 gather 控制,容易压垮下游服务
asyncio.gather 本身不提供限流能力。传入 1000 个协程,它就真的并发发起 1000 个请求——哪怕目标 API 有 QPS 限制或连接池上限。
真实场景中,应配合信号量(asyncio.Semaphore)或使用 asyncio.to_thread + 批处理逻辑做节流:
sem = asyncio.Semaphore(10) # 最多 10 个并发 <p>async def limited_fetch(user_id): async with sem: return await fetch_user(user_id)</p><p>results = await asyncio.gather(*[limited_fetch(i) for i in range(100)])</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill8054" title="python-parallelization"><img src="https://img.php.cn/upload/skill/000/000/081/179181013081932.jpg" alt="python-parallelization" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill8054" title="python-parallelization" class="overflowclass">python-parallelization</a> <p class="overflowclass">将顺序 Python 代码转换为并行/并发实现,用于在需要并行化 Python 代码、提升性能时使用。</p> </div> <a rel="nofollow" href="/xiazai/skill8054" title="python-parallelization" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
- 漏掉
async with sem是高频错误,会导致限流失效 - 信号量必须在协程内部 acquire,不能在
gather外围加锁 - 如果任务本身已含重试逻辑,注意信号量持有时间可能延长,影响吞吐
传入的不是协程对象,而是协程调用结果,会导致同步阻塞
常见错误写法:asyncio.gather(fetch_user(1), fetch_user(2)) 看似没问题,但如果 fetch_user 是普通函数(非 async def),或者你误写了 fetch_user(1)() 这类双括号,就会出问题。
关键判断点:传给 gather 的每个参数,必须是「协程对象(coroutine object)」,即调用后尚未 await 的状态:
- ✅ 正确:
fetch_user(1)(假设它是async def) - ❌ 错误:
await fetch_user(1)(这是 await 表达式,返回的是结果,不是协程) - ❌ 错误:
sync_func(1)(同步函数返回的是普通值,不是协程) - ❌ 错误:
fetch_user(1)()(试图对协程对象再调用,报TypeError: 'coroutine' object is not callable)
不确定时,可以用 inspect.iscoroutine() 检查:
import inspect assert inspect.iscoroutine(fetch_user(1)), "fetch_user must be async"
与 asyncio.create_task 对比:何时该用哪个?
asyncio.gather 适合「一次性并发一批已知任务,等全部结束」;create_task 更适合需要中间控制、取消、或动态增减任务的场景。
例如:你有一组任务,但想在某个任务完成后立刻启动下一个衍生任务(如下载完 A 再解析 A 得到 B 的 URL),就不能只靠 gather —— 它没有提供单个任务完成的回调钩子。
-
gather:简单、声明式、适合批处理,返回结果顺序与输入顺序严格一致 -
create_task+asyncio.wait或asyncio.as_completed:可响应单个完成事件、支持超时/取消、结果顺序按完成先后 - 混合用法常见:先用
create_task启动一批长期运行任务,再用gather等待其中几个关键依赖项
别为了“看起来更高级”而弃用 gather——它的语义清晰、开销低,绝大多数批量并发场景它就是最直接的解法。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










