← 返回题目列表

asyncio.gather、wait、as_completed 和 TaskGroup 有什么区别?

高频 困难 第 13 / 27 题 更新于 2026/07/27
gatherTaskGroupwaitas_completed

简化版

gather 适合并发执行一组任务并按输入顺序收集结果;wait 更偏底层,可以等待一批任务达到完成条件;as_completed 适合谁先完成先处理;TaskGroup 提供结构化并发,任务组内有异常时会统一取消和收尾,更适合现代 Python 中管理一组相关任务。

详细版

gather

results = await asyncio.gather(fetch_a(), fetch_b())

结果顺序和输入顺序一致。

as_completed

tasks = [asyncio.create_task(fetch(url)) for url in urls]
for task in asyncio.as_completed(tasks):
    result = await task
    handle(result)

谁先完成先处理。

wait

done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)

可以控制等待到第一个完成、全部完成或第一个异常。

TaskGroup

async with asyncio.TaskGroup() as tg:
    task1 = tg.create_task(fetch_a())
    task2 = tg.create_task(fetch_b())

任务组退出时会等待组内任务完成;如果任务出错,会更系统地处理取消和异常聚合。

面试回答重点:gather 重结果聚合,as_completed 重完成顺序,wait 重等待条件,TaskGroup 重结构化生命周期管理。

完整版教学

一、为什么需要多种并发组合方式

异步任务组合有不同需求:

  • 我想拿到所有结果;
  • 我想谁先回来先处理;
  • 我只等第一个成功结果;
  • 任何一个失败就取消全部;
  • 我希望一组任务有明确生命周期。

这些需求不同,所以 asyncio 提供了多种工具。

二、gather 适合收集一组结果

gather 是最常见的批量并发工具:

async def main():
    user, orders = await asyncio.gather(
        get_user(),
        get_orders(),
    )

它会并发运行这些 awaitable,并把结果按输入顺序返回。即使 get_orders() 先完成,返回列表里仍然在第二个位置。

如果其中一个任务抛异常,默认情况下 gather 会把异常传播给等待者。异常和取消细节要按具体版本与参数理解,工程里要明确是否使用 return_exceptions=True,不要模糊处理。

三、as_completed 适合流式处理结果

如果有 100 个 URL,谁先下载完就先解析,适合:

tasks = [asyncio.create_task(fetch(url)) for url in urls]

for task in asyncio.as_completed(tasks):
    try:
        result = await task
        handle(result)
    except Exception as exc:
        log_error(exc)

它的优势是降低尾部慢任务对整体处理的影响。搜索聚合、批量请求、并发爬取里经常用。

四、wait 更像等待条件工具

asyncio.wait() 返回两组任务:已完成和未完成。

done, pending = await asyncio.wait(
    tasks,
    return_when=asyncio.FIRST_COMPLETED,
)

它适合更底层的控制,比如:

  • 等第一个任务完成;
  • 等第一个异常;
  • 做自定义取消策略;
  • 对 pending 任务继续处理。

但日常“并发拿所有结果”用 gather 更清晰。

五、TaskGroup 为什么更现代

TaskGroup 引入了结构化并发思想。任务不再散落在各处,而是归属于一个明确作用域:

async with asyncio.TaskGroup() as tg:
    a = tg.create_task(fetch_a())
    b = tg.create_task(fetch_b())

离开 async with 时,任务组会确保内部任务完成或按规则收尾。如果某个任务失败,任务组会处理其他任务取消和异常聚合,让错误传播更有结构。

这比随手 create_task() 然后忘记管理更可靠。

六、怎么选

可以这样判断:

  • 简单并发拿全部结果:gather
  • 谁先完成先处理:as_completed
  • 需要 done/pending 和等待条件:wait
  • 一组相关任务要生命周期统一:TaskGroup
  • 后台长期任务:要设计关闭和异常处理,不要只靠 create_task

七、常见误区与追问

工具结果顺序异常/取消关注点
gather默认按传入顺序汇总一个失败时异常传播,其他任务处理要看具体状态
as_completed按完成先后逐个产出适合边完成边处理
wait返回 done/pending 集合更像底层等待条件工具
TaskGroup结构化并发子任务失败会取消同组任务并聚合异常
  • 误区:gather 会按完成顺序返回结果。 gather 的结果顺序对应传入 awaitable 的顺序,不是谁先完成谁先排前面。
  • 误区:as_completed 适合需要输入顺序的批处理。 它强调完成即处理,天然打乱顺序;需要顺序结果时要自己携带索引或用 gather
  • 误区:wait 会直接返回任务结果。 wait 返回的是 done 和 pending 两组任务,结果还需要对 done 中的 task 调 result() 或处理异常。
  • 追问:TaskGroup 比 gather 新在哪里? 它强调结构化并发,任务作用域清晰,某个子任务失败时同组任务会被协作取消,错误也更集中。
  • 追问:什么时候用 wait? 需要 FIRST_COMPLETED、超时后保留 pending、自己决定取消或继续等待时,wait 更灵活。
  • 追问:批量请求接口怎么选? 要所有结果并保持输入顺序,用 gather;要谁先回来先处理,用 as_completed;要强结构化生命周期,用 TaskGroup

记忆钩子:gather 收齐结果,as_completed 先到先处理,wait 给你任务集合,TaskGroup 管住任务生命周期。

八、加强记忆

异步任务组合要按目的选工具:gather 管结果列表,as_completed 管完成顺序,wait 管等待条件,TaskGroup 管一组任务的结构化生命周期。真正成熟的异步代码,不只是把任务跑起来,还要把异常、取消和收尾管起来。