asyncio.gather实现并发需协程真正await暂停,否则同步代码会阻塞事件循环;出错时默认中断并取消其余任务,可设return_exceptions=True捕获异常;它与create_task+wait的区别在于高层封装vs底层控制,且不支持限流,须用Semaphore。

直接用 asyncio.gather 就行,但它不是“自动并发”的魔法开关——协程必须真正是 await 暂停的(比如网络 I/O、asyncio.sleep),否则会变成串行执行。
为什么 await 之后的任务才真正并发?
asyncio.gather 只是把多个协程对象统一调度进事件循环,并不改变它们内部行为。如果某个协程里全是同步代码(比如 time.sleep(2) 或密集计算),它会阻塞整个事件循环,其他任务无法推进。
- ✅ 正确:协程内使用
await asyncio.sleep(1)、await aiohttp.ClientSession.get(...)、await aiomysql.connect(...) - ❌ 错误:协程内写
time.sleep(1)、sum(range(10**8))、或没加await的异步调用
如何处理 gather 中某个协程出错?
默认情况下,只要有一个协程抛异常,asyncio.gather 就立刻中断并抛出该异常,其余任务会被取消(即使它们已开始运行)。
- 用
return_exceptions=True让错误也作为结果返回:results = await asyncio.gather(task1(), task2(), return_exceptions=True),此时results列表里对应位置可能是Exception实例 - 不设该参数时,想保底执行完所有任务,得提前在每个协程里用
try/except捕获并转为返回值(比如return None或{"error": str(e)}) - 注意:即使设置了
return_exceptions=True,被取消的任务仍会触发CancelledError,需在协程内捕获以防日志刷屏
gather 和 create_task + wait 有什么区别?
asyncio.gather 是高层封装,适合“等全部完成并收集结果”;create_task + asyncio.wait 更底层,适合需要细粒度控制生命周期的场景(比如超时、按完成顺序处理、取消部分任务)。
立即学习“Python免费学习笔记(深入)”;
-
gather返回结果顺序严格对应传入协程的顺序,不管谁先完成 -
asyncio.wait返回的是已完成和未完成的Task集合,需手动task.result()获取结果,顺序不保证 - 性能上无本质差异,但
gather内部也是基于create_task实现的,多一层抽象 - 如果要限制并发数(比如最多同时跑 3 个 HTTP 请求),
gather本身不支持,得用asyncio.Semaphore包裹每个协程
最常被忽略的一点:协程对象不能重复 await。传给 gather 的必须是协程对象(即调用后返回的那个 coro),而不是已经 await 过一次的值。写成 await asyncio.gather(coro1, coro2) 对;写成 await asyncio.gather(await coro1, await coro2) 就彻底串行了,而且第二句会报 RuntimeWarning: coroutine was never awaited。


















