当前位置: 首页 > news >正文

python中,asyncio.create_task和await的区别与联系

这是 Python 异步编程 (asyncio) 最核心的两个概念,它们不是对立关系,而是配合使用的关系,一个负责启动并发任务,一个负责等待任务完成
 
我用最通俗、最清晰的方式给你讲透:
 

 

一、核心一句话总结

  1. await等待一个异步操作完成阻塞当前协程,直到结果返回(不主动创建并发)。
  2. asyncio.create_task()将协程包装成任务并扔进事件循环立刻后台运行真正实现并发)。
 
联系create_task 创建任务后,最终必须用 await 来等待任务执行完毕、获取结果。
 

 

二、详细对比

1. 作用不同

  • await 异步函数 ()
     
    • 只是调用并等待一个异步函数
    • 不会并发,执行完一个才会执行下一个
    • 类似同步的函数调用
     
  • asyncio.create_task (异步函数 ())
     
    • 把协程变成任务立刻调度执行
    • 不会阻塞当前流程,任务在后台跑
    • 这才是实现多任务并发的关键
     

2. 执行效果不同(最直观)

假设有两个耗时 1 秒的任务:
  • 只用 await:总耗时 2 秒(串行)
  • 先用 create_task,再 await:总耗时 1 秒(并行)

3. 执行时机不同

  • await 异步函数:调用时才开始执行
  • create_task调用后立刻在后台执行

 

三、代码示例(一看就懂)

示例 1:只用 await(串行,无并发)

import asyncio
import timeasync def task(name, delay):await asyncio.sleep(delay)print(f"任务 {name} 完成")async def main():start = time.time()# 直接 await,串行执行,一个做完再做下一个await task("A", 1)await task("B", 1)print(f"总耗时:{time.time() - start:.2f}s")asyncio.run(main())
 
 
任务 A 完成
任务 B 完成
总耗时:2.01s
 

 

示例 2:用 create_task + await(并发)

import asyncio
import timeasync def task(name, delay):await asyncio.sleep(delay)print(f"任务 {name} 完成")async def main():start = time.time()# 创建任务,立刻后台运行t1 = asyncio.create_task(task("A", 1))t2 = asyncio.create_task(task("B", 1))# 等待两个任务都执行完毕await t1await t2print(f"总耗时:{time.time() - start:.2f}s")asyncio.run(main())
 
 
任务 A 完成
任务 B 完成
总耗时:1.01s
 

 

四、关键联系(必须掌握)

  1. create_task 必须配合 await 使用
     
    • create_task 只是启动任务
    • 如果不 await 任务,程序可能直接结束,任务来不及执行完
     
  2. await 可以等待两种对象
     
    • 协程对象:await func()
    • 任务对象:await task
     
  3. 真正的并发 = create_task + await
     
    • 只 await:串行
    • 只 create_task,不 await:任务可能不执行完
    • create_task + await:并发执行 + 等待完成

五、分析本质原理(必须掌握)

以上第一段await串行的代码之所以会表现出串行执行(总耗时约3秒),其本质原理在于 await 关键字的“阻塞”特性 以及 事件循环(Event Loop)的调度机制
简单来说,await 告诉事件循环:“在这个任务完成之前,请不要执行我(main 协程)后面的任何代码。
以下是结合 asyncio 底层机制的深度分析:

1. await 的本质:控制权交还 + 暂停当前协程

当你写下 await task("A", 2) 时,发生了以下一系列动作:
  1. 调用协程task("A", 2) 被调用,生成一个协程对象。
  2. 开始执行main 协程开始执行 task A 的代码。
  3. 遇到挂起点:执行流进入 task A 内部,遇到了 await asyncio.sleep(delay)
  4. 交出控制权await 使得 task A 暂停,并将控制权交还给事件循环。此时,事件循环知道“task A 需要等待 2 秒”。
  5. 关键点:因为 main 协程是在 await task("A", 2) 这一行暂停的,所以 main 协程的状态被“冻结”在这一行。事件循环虽然拿回了控制权,但它无法越过这行代码去执行 main 函数里的下一行(即 await task("B", 1))。
结论await 实际上是把当前协程(main)挂起,直到被等待的对象(task A)彻底结束。

2. 事件循环的“单线程”调度逻辑

asyncio 运行在单线程上。事件循环就像一个不知疲倦的调度员,它的工作逻辑非常单纯:
  • 循环检查:查看当前有哪些任务是“就绪”的。
  • 执行任务:运行一个任务,直到它遇到 await 并暂停,或者运行结束。
  • 切换:如果有其他任务就绪,就切换过去;如果没有,就等待 I/O 事件。
在你提供的代码中,执行流程如下表所示:
时间 (秒) 事件循环正在做什么 main协程的状态 task A的状态 task B的状态
0.0 启动 main 运行中 未创建 未创建
0.0 main 调用 task A 暂停 (在 await A 处) 运行中 未创建
0.0 task A 遇到 sleep(2) 暂停 暂停 (等待2秒) 未创建
0.0-2.0 事件循环发现无事可做,等待 2秒 暂停 暂停 未创建
2.0 task A 唤醒,打印结束 暂停 结束 未创建
2.0 task A 完成,main 恢复 运行中 (继续向下) 已结束 未创建
2.0 main 调用 task B 暂停 (在 await B 处) 已结束 运行中
2.0 task B 遇到 sleep(1) 暂停 已结束 暂停 (等待1秒)
2.0-3.0 事件循环等待 1秒 暂停 已结束 暂停
3.0 task B 唤醒,打印结束 暂停 已结束 结束
3.0 task B 完成,main 结束 结束 已结束 已结束

3. 为什么没有并发?

并发的核心在于 “在等待 A 的时候去执行 B”
在你的代码中,task B 甚至还没有被创建(代码还没运行到那一行),因为 main 协程被死死地卡在 await task("A", 2) 这一行。
  • 串行模式 (await 直接调用)
    main 就像一个只会听指令的机器人,它必须等 A 做完,才敢迈出下一步去叫 B。
Ttotal=TA+TB=2+1=3
  • 并发模式 (create_task)
    如果使用 create_task,相当于 main 告诉事件循环:“把 A 和 B 都安排上”,然后 main 自己去睡觉(await 等待两者完成)。此时 A 和 B 都在事件循环的待办列表中,事件循环可以在 A 睡觉的时候去运行 B。
Ttotal=max⁡(TA,TB)=max⁡(2,1)=2

4. 总结

await 串行的本质原理是:当前协程(main)的执行流被阻塞了
虽然 asyncio.sleep 本身是非阻塞的(它不会卡死整个 Python 程序,事件循环还在转),但它阻塞了调用它的那个协程的后续代码执行。只有当 task A 彻底完成后,main 协程才会从“冻结”状态解冻,继续执行下一行代码去启动 task B
http://www.jsqmd.com/news/733037/

相关文章:

  • 024、多工具协调:Agent的规划与执行
  • CA证书
  • 避坑指南:在Ubuntu 22.04上为通义千问安装flash-attention,我踩过的那些环境依赖的坑
  • VinXiangQi象棋连线工具:基于YOLOv5的智能对局助手深度解析
  • 当熔断器遇见分支预测:两种“猜错就惩罚”的系统哲学
  • 终极解码方案:如何让老旧电脑流畅播放4K HDR视频?
  • 告别公网IP烦恼:用cpolar在Windows上SSH远程连接家里CentOS服务器(保姆级图文教程)
  • JWT原理与Token
  • 荧光标记磷脂(Cy3/Cy5/FITC)及其性质科普
  • 甘肃省 CPPM 报名(美国采购协会)SCMP 报名(中物联)授权招生报名中心及联系方式 - 众智商学院课程中心
  • 神经网络中的微分运算原理与实践
  • 终极指南:Cursor Pro破解工具完整方案,5步实现AI编程助手永久免费使用
  • 观察 Taotoken 按 token 计费模式如何实现精准的成本控制
  • Mysql常见问题汇总(3)-索引/查询优化篇
  • Visual C++运行库:Windows程序的“隐形桥梁“如何影响你的日常使用?
  • 无与不的辩证法
  • 体验 Taotoken 多模型聚合带来的稳定与低延迟响应
  • 轻松搞定Mac飞秋安装:告别配置困扰的智能方案
  • Java程序员72小时Python实战手册
  • RT809H编程器提取固件翻车实录:从识别失败到成功读取,我踩了哪些坑?
  • springboot+nodejs微信小程序的睡眠失眠助眠音乐系统
  • 仅限首批通过MCP 2026认证的23家企业的内部文档节选(含真实权限爆炸图谱与自动收敛算法伪代码)
  • 手把手教你为STM32H7自制飞控板移植PX4固件(基于NuttX系统)
  • 二层交换机、三层交换机和路由器到底有啥不一样?用大白话给你讲透
  • PowerToys中文优化指南:告别英文界面,让Windows效率提升200%
  • 别再死记硬背卡诺图了!用这个十字路口红绿灯电路,带你真正搞懂组合逻辑设计
  • 从零构建MCP 2026集成中枢:用1个OpenAPI 3.1 Schema驱动6大系统联动,附可运行Terraform IaC模板
  • Moonlight-PC:揭秘Java跨平台游戏串流技术架构的7大核心设计
  • 深入理解BiRefNet:高分辨率二值化图像分割的核心架构与实践指南
  • 测了6款AI图文笔记工具,我发现90%都在浪费时间