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 小时前
具身智能的学习 - 完成一个机械臂 第1节
python·学习·机械臂·具身智能
小小张说故事2 小时前
Python 的 class 语句到底执行了什么?拆开 __prepare__、metaclass.__new__ 和那个被讲反的 __set_name__ 顺序
python
天天被压力4 小时前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力4 小时前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python
特立独行的猫A4 小时前
AI 时代,我为什么推荐用 cj-tauri 开发桌面应用
java·后端·python
小lo想吃棒棒糖4 小时前
把机器人训练从 GPU 搬到昇腾 NPU:启元 Q1 起身动作 PPO 训练全记录
python·机器人·昇腾·启元机器人
Thanks_ks5 小时前
【第 006 讲】搭建 PyCharm 开发环境:PyCharm 的下载与安装 | 初始设置与常用配置
ide·python·pycharm·开发工具·jetbrains·开发环境·集成开发环境
青少儿编程课堂6 小时前
蓝桥杯青少组图形化省考报名倒计时·全国青少年信息素养大赛·电子学会图形化考级:校园黑白棋人机对战解析
c++·python·算法·bfs·信息学竞赛
tellmewhoisi6 小时前
nlp:知识库检索和dify知识库创建
python·自然语言处理
2601_968793146 小时前
Python Requests 轻量自动化:无需浏览器,实现真人级账号浏览模拟
开发语言·python·自动化