asyncio.gather 的核心功能是并发运行多个可等待对象(awaitables),并等待它们全部完成后收集结果。
当你需要同时执行多个独立的异步操作(比如并发发出多个网络请求或数据库查询)并一次性获取所有结果时,asyncio.gather 是理想的选择。
asyncio.gather 的主要作用
并发执行:
gather接收一个或多个可等待对象(如协程、任务或 Future)作为参数,并将它们并发地在事件循环中调度执行。 如果传入的是协程,gather会自动将其包装成asyncio.Task。等待完成:
await asyncio.gather(...)会阻塞当前任务,直到所有传入的可等待对象都执行完毕。收集结果:一旦所有任务都成功完成,
gather会返回一个列表,其中包含了每个任务的返回值。 重要的是,返回结果的顺序与你传入可等待对象的顺序完全一致,而与它们的实际完成顺序无关。
如何使用 asyncio.gather
最常见的使用方式是将多个协程调用作为参数传递给 gather。
基础代码示例
1 | import asyncio |
运行结果分析:
- 两个
fetch_data任务会并发执行。 - 程序不会等待2秒再等待3秒(总共5秒),而是会等待最长的那个任务完成,也就是3秒。
- 最终的输出结果
results会是一个列表['来自 API 的数据', '来自 数据库的数据'],顺序与调用gather时的参数顺序一致。
异常处理
asyncio.gather 在处理异常时有两种模式,由 return_exceptions 参数控制。
默认行为 (return_exceptions=False)
默认情况下,如果 gather 中有任何一个任务引发了异常,这个异常会立即被传播到 await asyncio.gather(...) 的调用处。 这意味着:
- 你的程序会立即因为这个异常而中断(除非你用
try...except捕获它)。 - 其他仍在运行的任务不会被自动取消,它们会继续在后台运行直到完成或事件循环结束。
1 | async def failing_coroutine(): |
运行结果分析:
failing_coroutine在1秒后抛出ValueError。gather立即将这个异常传播出来,并被try...except块捕获。fetch_data("API", 2)任务不会被取消,它会继续运行并在2秒后打印完成信息。
收集异常 (return_exceptions=True)
当你需要确保所有任务都执行完毕,无论它们是否成功,并且想要检查每个任务的结果时,可以将 return_exceptions 设置为 True。
在这种模式下:
gather不会传播异常,而是会像对待成功结果一样“收集”它们。- 返回的列表中,成功任务的位置是其返回值,而失败任务的位置则是该异常对象。
1 | async def main_return_exceptions(): |
运行结果分析:
程序会等待所有任务完成(大约2秒),然后 results 列表会包含类似 ['来自 API 的数据', ValueError('任务发生错误!')] 的内容。你可以遍历这个列表来分别处理成功和失败的情况。
asyncio.gather vs asyncio.wait
asyncio 还有一个 asyncio.wait() 函数,它与 gather 类似但用途不同:
- 返回值:
gather直接返回结果列表;wait返回两组Task对象:已完成的(done)和未完成的(pending)。你需要自己从已完成的任务中提取结果或异常。 - 灵活性:
wait更加灵活,可以配置为在第一个任务完成或第一个任务出现异常时就返回,而gather总是等待所有任务完成。 - 易用性:对于“运行一堆任务并获取所有结果”这个常见用例,
gather的接口更简单直接。