asyncio并发协程

asyncio并发协程

1 简单使用

asyncio.gather / create_task / as_completed 核心区别

三者都是并发执行协程 ,但任务创建时机、结果返回顺序、等待方式、异常处理 完全不一样。 前提:协程函数调用 func() 只是得到协程对象,不会自动运行;必须交给事件循环调度。

1.1 asyncio.create_task(coro)

作用:立刻把协程包装成 Task,丢进事件循环后台调度

复制代码
import asyncio

async def foo(n):
    await asyncio.sleep(n)
    return n

async def main():
    # 创建任务,立即开始后台执行
    t1 = asyncio.create_task(foo(1))
    t2 = asyncio.create_task(foo(2))
    
    res1 = await t1
    res2 = await t2
    print(res1, res2)

asyncio.run(main())

✅ 特点

  1. 调用 create_task 的一瞬间,任务就已经开始跑了(不是等到 await 才跑)
  2. 返回 Task 对象,可以:await task、task.cancel()、task.done()、task.result()
  3. 精细控制:单独取消、单独判断完成状态、获取异常
  4. 多个 task 之间天然并发

❌ 缺点

  • 需要手动管理多个 Task 变量,批量收集结果麻烦

一句话:手动创建后台任务,细粒度控制


1.2 asyncio.gather(*coros_or_tasks)

作用:批量并发,等待所有任务全部完成,一次性返回全部结果列表

复制代码
async def main():
    res = await asyncio.gather(
        foo(1),
        foo(2),
        foo(0.5)
    )
    print(res) # [1,2,0.5],顺序和传入顺序一致,和谁先跑完无关

✅ 特点

  1. 接收一堆协程/Task,自动并发执行
  2. 返回结果列表顺序 = 传入参数顺序,和任务完成先后无关
  3. 默认:任意一个任务抛异常,gather 立刻抛出异常,其他任务继续在后台跑(不会自动cancel)
  4. 可选参数 return_exceptions=True:把异常当成结果返回,不中断整体

❌ 缺点

  • 必须等全部任务结束才能拿到结果,不能先拿到先完成的任务结果
  • 无法逐个处理已完成任务

一句话:批量并发,等全部完成,按输入顺序打包返回结果
补充:gather 内部底层也会用 create_task 把传入的协程包装成 Task。


1.3 asyncio.as_completed(fs)

作用:迭代器,哪个任务先完成,就先 await 拿到它的结果

复制代码
async def main():
    tasks = [
        asyncio.create_task(foo(1)),
        asyncio.create_task(foo(2)),
        asyncio.create_task(foo(0.5)),
    ]
    for coro in asyncio.as_completed(tasks):
        res = await coro
        print(res) # 先输出0.5,再1,再2,谁先跑完谁先出来

✅ 特点

  1. 返回一个迭代器,循环里 await 迭代项,按完成顺序返回结果
  2. 适合流式处理:任务跑完一个就处理一个,不用等全部
  3. 传入的可以是协程或者Task;如果传协程,as_completed内部会自动包装成Task

❌ 缺点

  • 结果顺序不再保证和输入一致
  • 异常:await coro 的时候才抛出对应任务的异常,可以单独捕获单个任务错误

一句话:谁先跑完谁先返回,流式逐个处理结果

2 横向对比表

特性 create_task gather as_completed
任务启动时机 调用时立刻调度 await gather 时全部启动 迭代器创建时就启动
获取结果 await 单个Task 全部完成后一次性返回列表 逐个获取,先完成先返回
结果顺序 由你await顺序决定 严格等于传入顺序 按完成先后,无序
等待策略 可单独await 等待所有完成 逐个等待,不等待全部
异常 await task时抛出 默认任一失败直接抛;return_exceptions可捕获 await 当前项时抛出单个任务异常
使用场景 需要单独取消、查询状态的任务 并发一批任务,全部结果都要,顺序要对齐 任务耗时差异大,跑完一个处理一个

3 典型场景选择建议

  1. 要单独取消任务、查询任务状态 → create_task
  2. 并发一批任务,需要全部结果,并且结果顺序和输入保持一致 → gather
  3. 任务时长差别大,想拿到一个结果就立刻处理一个,不等全部 → as_completed

4 容易踩坑点

  1. ❌ 错误写法:await foo(1); await foo(2),这是串行,不是并发!必须交给 create_task / gather / as_completed 才并发。
  2. gather 遇到异常,其余任务不会自动取消;想要第一个失败就取消所有任务,需要自己捕获异常然后遍历 task.cancel()。
  3. as_completed 如果传入协程对象(不是Task),内部会自动创建Task,但你拿不到Task句柄,无法cancel。想要支持取消,提前手动 create_task。
相关推荐
小小张说故事1 小时前
Python 的整数为什么永远不会溢出?拆开大整数对象,看 30 位一节的存储与运算代价
python
用户298698530142 小时前
Python 实现 Word 与 TIFF 双向转换的方案
后端·python·api
benchmark_cc2 小时前
实时行情和分钟 K 线应该如何配合使用?从信号计算到交易决策的完整分析
大数据·python·数据分析·pandas·量化交易·股票数据·quantdash
LMT流氓兔2 小时前
第四单元——第17课:让回答边生成、边显示
服务器·数据库·人工智能·python·oracle·langchain
MetWeave2 小时前
从零跑一个气象数据管道:抓取、清洗、对账、节奏
后端·python
用户3851408539472 小时前
Python 的 O_NOFOLLOW 为什么挡不住中间目录的符号链接?
python
Generalzy3 小时前
Qt 桌面开发入门:PySide6 学习路线、架构原理与技术选型
笔记·python·学习
Mr.D学长3 小时前
毕业设计项目 基于机器视觉的车牌识别系统
python·毕业设计·毕设
云和数据.ChenGuang3 小时前
AI 工程师相关就业岗位(细分,偏工程落地,由底层到上层)
服务器·开发语言·人工智能·python