asyncio.gather需传入未await的协程对象,否则报typeerror;默认一异常即中断所有任务,加return_exceptions=true可获混合结果;须用semaphore控并发防压垮服务。

直接用 asyncio.gather 就行,但它不是“自动并发”的银弹——你得确保每个任务本身是协程、不阻塞、且没有意外同步调用。
为什么 asyncio.gather 有时像没并发?
常见错因是传了普通函数或含 time.sleep()、requests.get() 这类同步阻塞调用。这些会卡住整个事件循环,让其他协程无法调度。
- ✅ 正确:传入
async def定义的协程对象(如fetch_url(url)) - ❌ 错误:传入
requests.get(url)或time.sleep(1)—— 必须换成aiohttp.ClientSession.get()和asyncio.sleep() - ⚠️ 注意:
asyncio.gather(*coros)中的*coros是解包协程对象,不是调用后结果;写成gather(fetch_url(a), fetch_url(b))才对,别写成gather(fetch_url(a)())
如何控制并发数量避免压垮服务?
asyncio.gather 默认全量并发,但目标 API 通常有速率限制或连接池上限。硬上 100 个请求大概率触发 ConnectionResetError 或 429。
- 用
asyncio.Semaphore限流:创建sem = asyncio.Semaphore(10),每个协程开头加async with sem: - 别依赖
gather自带节流——它没有内置并发数参数 - 示例片段:
async def limited_fetch(url): async with sem: return await aiohttp_get(url) results = await asyncio.gather(*[limited_fetch(u) for u in urls])
错误处理:一个失败是否中断全部?
默认行为是「任一异常即中断」,整个 gather 抛出第一个异常,其余任务被取消——这常导致资源泄漏或调试困难。
- 加参数
return_exceptions=True:失败项返回异常对象而非抛出,后续任务继续执行 - 之后手动检查结果:
for r in results: if isinstance(r, Exception): print("fail:", r) - 注意:即使设了
return_exceptions=True,已启动但未完成的任务仍可能被取消(取决于取消时机),建议在协程内用try/finally做清理
真正难的不是写 gather 这一行,而是确保每个子任务都干净地挂起、恢复、超时可控、错误可追溯——尤其混合了第三方库时,得逐个确认它们是否真支持异步。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











