async 和 await 详细解析

Pythonasyncawait详细解析

asyncawait是 Python 3.5 及以上版本引入的异步编程核心语法,用于实现「非阻塞式」代码执行,解决传统同步编程中高IO等待(如网络请求、数据库操作、文件读写)导致的资源浪费问题,大幅提升程序在高IO场景下的并发性能。

一、核心概念铺垫:同步 vs 异步

在理解async/await之前,先明确同步编程和异步编程的核心差异:

1. 同步编程(默认模式)

  • 执行逻辑:代码按顺序串行执行,一个任务执行完成(包括等待时间)后,下一个任务才会开始。
  • 典型问题:遇到IO等待(如请求接口、读取大文件)时,整个程序会「阻塞」在该任务上,浪费CPU资源。
    示例:同步请求3个接口,每个接口耗时2秒(其中1.9秒是网络等待),总耗时约6秒,CPU在等待期间处于闲置状态。

2. 异步编程(async/await实现)

  • 执行逻辑:代码按事件循环驱动的非阻塞模式执行,当一个任务遇到IO等待时,不会阻塞整个程序,而是将控制权交还给事件循环,去执行其他就绪任务,待IO任务完成后,再继续执行该任务的后续代码。
  • 核心优势:在IO等待期间充分利用CPU资源,提升程序并发处理能力。
    示例:异步请求3个接口,总耗时约2秒(接近单个接口的耗时),CPU在等待期间可处理其他任务。

二、async:定义异步函数/协程

1. 核心定义

async是一个关键字,用于定义异步函数(也称为「协程函数」,Coroutine Function),标志着该函数内部包含非阻塞执行的逻辑,不能直接同步调用执行,必须通过异步方式触发。

2. 语法格式

# 定义异步函数(协程函数)asyncdef异步函数名(参数列表):# 函数体(可包含 await 语句,也可不含)执行逻辑return返回值

3. 关键特性

  • 异步函数被调用时,不会立即执行函数体,而是返回一个「协程对象(Coroutine Object)」,这是异步函数与普通同步函数的核心区别。
  • 异步函数的返回值不能通过普通=赋值获取,必须通过await或异步框架的相关方法获取。

4. 示例:定义异步函数

# 定义一个简单的异步函数asyncdefasync_hello():print("进入异步函数")# 模拟IO等待(后续会用 asyncio.sleep 替代,这里先演示结构)awaitasyncio.sleep(1)print("异步函数执行完成")return"异步函数返回结果"# 直接调用异步函数,不会执行函数体,仅返回协程对象coro=async_hello()print("调用异步函数返回的结果:",coro)# 输出:调用异步函数返回的结果: <coroutine object async_hello at 0x10xxxxx>

5. 协程的本质

协程(Coroutine)是「轻量级线程」,由Python程序自身控制(而非操作系统内核控制),切换开销远小于传统线程。异步函数就是协程的载体,async关键字的核心作用是标记该函数为协程函数,使其能被事件循环调度执行。

三、await:挂起协程等待异步任务完成

1. 核心定义

await是一个关键字,只能在异步函数(async def定义的函数)内部使用,用于「挂起当前协程的执行」,等待一个「可等待对象(Awaitable)」执行完成并返回结果,在此期间,将控制权交还给事件循环,允许其他协程执行。

2. 核心作用

  1. 等待异步任务完成:阻塞当前协程(而非整个程序),直到被等待的对象执行完毕。
  2. 非阻塞式等待:在等待期间,事件循环可以调度其他就绪的协程执行,充分利用CPU资源。
  3. 获取异步任务返回值:await会接收被等待对象的返回值,可直接赋值给变量使用。

3. 可等待对象(Awaitable)

await后面只能跟「可等待对象」,否则会抛出TypeError,常见的可等待对象有3类:

  • 协程对象(Coroutine):async def函数调用后返回的对象(最常用)。
  • 任务对象(Task):由asyncio.create_task()loop.create_task()创建的对象,用于封装协程,支持并发执行多个协程。
  • 未来对象(Future):表示一个尚未完成的异步操作结果,通常用于底层异步编程(如异步IO框架实现)。

4. 语法格式

asyncdef异步函数名():# 等待可等待对象执行完成,并获取返回值结果=await可等待对象 后续执行逻辑

5. 示例:await等待协程执行

importasyncio# 定义异步函数(模拟IO任务)asyncdefsimulate_io_task(task_name,delay):print(f"开始执行异步任务:{task_name},等待{delay}秒")# await 等待 asyncio.sleep(异步睡眠,非阻塞)awaitasyncio.sleep(delay)# asyncio.sleep 是异步函数,返回协程对象print(f"异步任务:{task_name}执行完成")returnf"{task_name}返回结果"# 定义主异步函数asyncdefmain():print("===== 开始执行主异步函数 =====")# 依次等待3个异步任务执行(串行异步,总耗时约 1+2+3=6 秒)result1=awaitsimulate_io_task("任务1",1)result2=awaitsimulate_io_task("任务2",2)result3=awaitsimulate_io_task("任务3",3)# 打印返回结果print("\n===== 所有任务执行完成 =====")print("结果1:",result1)print("结果2:",result2)print("结果3:",result3)# 启动事件循环,执行主协程if__name__=="__main__":asyncio.run(main())

6. 关键注意点

  • await不能在同步函数中使用:如果在普通def定义的同步函数中使用await,会直接抛出语法错误。
  • await会挂起当前协程,而非整个程序:只有当前协程被阻塞,事件循环可以正常调度其他协程。
  • 多个await串行执行时,仍会按顺序等待:如上述示例,3个任务串行执行,总耗时等于各任务耗时之和,若要实现并发,需使用asyncio.create_task()封装任务。

四、异步编程的核心:事件循环(Event Loop)

asyncawait本身无法直接运行异步代码,必须依赖「事件循环」(Event Loop)—— 它是Python异步编程的「核心调度器」,负责管理所有协程的执行顺序、分发IO事件、切换协程等。

1. 事件循环的核心职责

  1. 注册和调度协程/任务。
  2. 监控协程的执行状态,当协程被await挂起时,切换到其他就绪协程。
  3. 当被挂起的协程完成IO等待后,将其重新加入就绪队列,等待继续执行。
  4. 终止整个异步程序的执行(所有任务完成后)。

2. 常用事件循环操作

在Python 3.7及以上版本,推荐使用asyncio.run()来启动事件循环,它会自动创建、运行和关闭事件循环,简化异步代码的启动流程。

示例:并发执行多个异步任务(使用asyncio.create_task()
importasyncioasyncdefsimulate_io_task(task_name,delay):print(f"开始执行异步任务:{task_name},等待{delay}秒")awaitasyncio.sleep(delay)print(f"异步任务:{task_name}执行完成")returnf"{task_name}返回结果"asyncdefmain():print("===== 开始执行主异步函数(并发模式) =====")# 1. 创建任务对象(将协程封装为Task,加入事件循环就绪队列)task1=asyncio.create_task(simulate_io_task("任务1",1))task2=asyncio.create_task(simulate_io_task("任务2",2))task3=asyncio.create_task(simulate_io_task("任务3",3))# 2. 并发等待所有任务完成(总耗时约 3 秒,等于最长任务的耗时)result1=awaittask1 result2=awaittask2 result3=awaittask3# 3. 打印返回结果print("\n===== 所有任务执行完成 =====")print("结果1:",result1)print("结果2:",result2)print("结果3:",result3)if__name__=="__main__":# asyncio.run() 自动创建事件循环,运行主协程,执行完毕后关闭事件循环asyncio.run(main())
执行结果分析
===== 开始执行主异步函数(并发模式) ===== 开始执行异步任务:任务1,等待 1 秒 开始执行异步任务:任务2,等待 2 秒 开始执行异步任务:任务3,等待 3 秒 异步任务:任务1 执行完成 异步任务:任务2 执行完成 异步任务:任务3 执行完成 ===== 所有任务执行完成 ===== 结果1: 任务1 返回结果 结果2: 任务2 返回结果 结果3: 任务3 返回结果
  • 3个任务几乎同时启动,总耗时约3秒(等于耗时最长的任务3),实现了真正的并发异步执行。
  • 这就是async/await的核心价值:在IO密集型场景下,通过非阻塞等待大幅提升并发效率。

五、async/await的使用场景与限制

1. 最佳使用场景(IO密集型任务)

async/await对IO密集型任务有显著性能提升,常见场景包括:

  • 网络请求:接口调用、爬虫、微服务通信(推荐使用aiohttp替代requests)。
  • 数据库操作:异步数据库查询(推荐使用asyncmy替代pymysqlasyncpg替代psycopg2)。
  • 文件读写:大文件异步读写(推荐使用aiofiles替代内置open)。
  • 消息队列:异步消费/生产消息。

2. 不适用场景(CPU密集型任务)

async/await对CPU密集型任务(如大数据计算、加密解密、图形渲染)几乎没有性能提升,甚至可能因为协程切换开销导致性能下降。

  • 原因:CPU密集型任务会持续占用CPU资源,不会产生IO等待,事件循环无法切换协程,无法发挥异步编程的优势。
  • 替代方案:对于CPU密集型任务,推荐使用multiprocessing(多进程)或concurrent.futures.ProcessPoolExecutor

3. 关键使用限制

  1. await只能在async def函数内部使用,不能在同步函数(def)或全局作用域中使用。
  2. 异步函数不能直接同步调用,必须通过asyncio.run()awaitasyncio.create_task()触发执行。
  3. 异步代码必须使用异步库:同步库(如requestspymysql)会阻塞事件循环,导致异步编程失效,必须使用对应的异步库(如aiohttpasyncmy)。
  4. 协程切换是「协作式」的,而非「抢占式」的:只有当协程遇到await挂起时,才会将控制权交还给事件循环,若一个协程长时间不挂起,会阻塞其他协程的执行。

六、async/await与同步代码的对比示例

1. 同步代码(requests调用接口)

importrequestsimporttimedefsync_request(url,delay):print(f"开始同步请求:{url}")# 同步请求(阻塞等待)response=requests.get(url)time.sleep(delay)# 模拟额外等待print(f"同步请求:{url}完成,状态码:{response.status_code}")returnresponse.status_codedefmain():start_time=time.perf_counter()# 串行执行3个同步请求sync_request("https://httpbin.org/get",1)sync_request("https://httpbin.org/get",1)sync_request("https://httpbin.org/get",1)end_time=time.perf_counter()print(f"\n总耗时:{end_time-start_time:.2f}秒")if__name__=="__main__":main()
执行结果

总耗时约 6~9 秒(包含网络请求时间+3秒睡眠时间),全程串行阻塞。

2. 异步代码(aiohttp调用接口)

importaiohttpimportasyncioimporttimeasyncdefasync_request(url,delay):print(f"开始异步请求:{url}")# 异步请求(非阻塞等待)asyncwithaiohttp.ClientSession()assession:asyncwithsession.get(url)asresponse:awaitasyncio.sleep(delay)# 异步睡眠(非阻塞)print(f"异步请求:{url}完成,状态码:{response.status}")returnresponse.statusasyncdefmain():start_time=time.perf_counter()# 并发执行3个异步请求task1=asyncio.create_task(async_request("https://httpbin.org/get",1))task2=asyncio.create_task(async_request("https://httpbin.org/get",1))task3=asyncio.create_task(async_request("https://httpbin.org/get",1))awaittask1awaittask2awaittask3 end_time=time.perf_counter()print(f"\n总耗时:{end_time-start_time:.2f}秒")if__name__=="__main__":asyncio.run(main())
执行结果

总耗时约 2~3 秒(包含网络请求时间+1秒睡眠时间),全程并发非阻塞,性能提升显著。

七、总结

  1. async:用于定义协程函数(异步函数),标记函数包含非阻塞逻辑,调用后返回协程对象,不立即执行。
  2. await:仅在协程函数内部使用,用于挂起当前协程,等待可等待对象完成,期间将控制权交还给事件循环,实现非阻塞等待。
  3. 核心价值:在IO密集型场景下,通过事件循环调度协程,充分利用CPU资源,大幅提升程序并发处理能力。
  4. 运行依赖:异步代码必须通过asyncio.run()启动事件循环,且需配合异步库使用,避免同步库阻塞事件循环。
  5. 适用边界:仅适用于IO密集型任务,CPU密集型任务推荐使用多进程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1098051.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot智慧旅游管理系统【附源码+文档】

&#x1f495;&#x1f495;作者&#xff1a; 米罗学长 &#x1f495;&#x1f495;个人简介&#xff1a;混迹java圈十余年&#xff0c;精通Java、小程序、数据库等。 &#x1f495;&#x1f495;各类成品Java毕设 。javaweb&#xff0c;ssm&#xff0c;springboot等项目&#…

全域适配,精准输出,这款组合导航重新定义精准数据

没有组合导航&#xff0c;飞行记录仪只是一个记录模糊数据的盒子&#xff0c;无法在发生事故后精准复盘;有了组合导航&#xff0c;飞行记录仪才能称之为“黑匣子”&#xff0c;能精准、完整、无断点的提供精准飞行数据。今天&#xff0c;这款组合导航将带你重新定义什么是绝对可…

微爱帮监狱寄信云存储技术实现方案

一、安全分层存储架构# 1. 智能分层存储引擎 class SecureCloudStorage:"""监狱通信数据分层存储系统"""def __init__(self):# 存储层配置self.layers {hot: {type: ssd,provider: aliyun_essd,encryption: AES-256-GCM,retention: 7天,cost_fa…

小白也能学会的PyTorch安装教程GPU版|Miniconda-Python3.10上手指南

小白也能学会的PyTorch安装教程GPU版&#xff5c;Miniconda-Python3.10上手指南 在深度学习的世界里&#xff0c;第一步往往不是写模型&#xff0c;而是——环境装不上。 你是不是也经历过这样的场景&#xff1a;跟着教程敲命令&#xff0c;结果 pip install torch 装完一跑…

程序员不怕BUG,怕的是老到没人要

一、跨年夜的生产环境&#xff1a;当所有人都在庆祝&#xff0c;只有你在值班最后一个需求终于上线了。办公室里只剩下你和服务器指示灯交替闪烁。手机屏幕上&#xff0c;朋友圈正在进行一场盛大的跨年直播——海岛的日落、山顶的日出、家庭的团聚、情侣的拥吻。而你&#xff0…

dbt+DataOps+StarRocks:构建一体化数据治理与智能分析平台实践

作者&#xff1a;胡翔&#xff0c;SJM Resorts 企业方案设计高级经理、dbt- starrocksContributor本文内容整理自 SJM Resorts 企业方案设计高级经理、dbt-starrocks Contributor 胡翔在 StarRocks Connect 2025 上的演讲。文章将主要围绕三个方面展开&#xff1a;dbt 在数据建…

Conda与Pip混合使用指南|Miniconda-Python3.10环境下PyTorch安装策略

Conda与Pip混合使用指南&#xff5c;Miniconda-Python3.10环境下PyTorch安装策略 在深度学习项目开发中&#xff0c;一个常见的场景是&#xff1a;你刚接手同事的代码仓库&#xff0c;满怀期待地运行 pip install -r requirements.txt&#xff0c;结果却卡在了 torch 安装环节…

SCI检索号怎么看?

SCI检索号是什么&#xff1f;它根据什么来分配&#xff0c;有哪些作用&#xff1f;SCI检索号和DOI号有什么区别&#xff1f;下面淘淘学术来给大家详细讲解这些问题。一、SCI检索号的基本概念SCI论文检索号是WOS数据库中对所收录SCI论文的身份编码&#xff0c;具有唯一性和不可替…

jmeter设置中文页面的办法?

打开 JMeter(确保已正确安装并启动,启动文件为 bin/jmeter.bat 或 bin/jmeter.sh)。 在顶部菜单栏中找到 Options(选项),点击展开下拉菜单。 在下拉菜单中选择 Choose Language(选择语言),此时会显示支持的语…

PyTorch安装教程GPU版:Miniconda-Python3.10镜像一键配置深度学习环境

PyTorch GPU环境一键搭建&#xff1a;MinicondaPython3.10镜像实战指南 在深度学习项目启动阶段&#xff0c;最让人头疼的往往不是模型设计&#xff0c;而是环境配置——“为什么你的代码在我机器上跑不起来&#xff1f;”这个问题几乎困扰过每一位AI开发者。依赖冲突、CUDA版本…

确保移动端适配良好以符合谷歌移动优先索引

确保移动端适配良好以符合谷歌移动优先索引 在今天的数字生态中&#xff0c;用户打开网页的第一选择早已不再是台式机浏览器。StatCounter 的数据显示&#xff0c;全球超过 55% 的网络流量来自手机和平板设备。这一转变不仅改变了用户的浏览习惯&#xff0c;也彻底重塑了搜索引…

Java毕设项目推荐-基于SpringBoot+vue招投标系统的设计与实现招标项目发布、投标文件提交、在线评标、合同管理全流程数字化招标方、投标方、评标专家、监管机构【附源码+文档,调试定制服务】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

HTTP中GET接口测试

JMeter GET接口测试极简笔记建基础:测试计划→右键加线程组→线程组右键加【HTTP请求】配请求:选GET,填域名/IP、接口路径,参数直接填参数面板(名+值)加元件:HTTP请求右键加【查看结果树】,再加HTTP信息头(UT…

在http中请求和响应包括几部分

HTTP 请求报文包含3部分:请求行、请求头、请求体(可选,GET请求一般无请求体)。 HTTP 响应报文包含3部分:状态行、响应头、响应体。

在禅道中如何新增测试用例?

在禅道中新增测试用例有单个 / 批量创建和导入创建两种主要方式,具体操作如下: 单个 / 批量创建 进入禅道系统,点击 “测试”-“用例”,选择 “建用例”。 在建用例页面,选择对应的产品、需求模块、用例类型、适用…

PyTorch模型微调实战:基于Miniconda-Python3.10环境复现SOTA结果

PyTorch模型微调实战&#xff1a;基于Miniconda-Python3.10环境复现SOTA结果 在深度学习领域&#xff0c;我们常常面临这样的窘境&#xff1a;论文中宣称的SOTA&#xff08;State-of-the-Art&#xff09;性能&#xff0c;在自己的机器上却始终无法复现。训练精度差几个百分点、…

斯坦福大学发现:AI系统分工模式的信息论奥秘

这项由斯坦福大学计算机科学系Shizhe He领导的研究团队发表于2024年12月25日&#xff0c;论文标题为"An Information Theoretic Perspective on Agentic System Design"。研究团队包括来自斯坦福大学计算机科学系、统计系和Wu Tsai神经科学研究所的多位专家。有兴趣深…

HTTP Keep-Alive 笔记

一、 核心含义 Keep-Alive就是HTTP长连接,让TCP连接复用,一次连好多次传数据,不用每次请求都重新握手断开。 对应HTTP1.0需手动加Connection:Keep-Alive开启,HTTP1.1默认开启(默认Connection:keep-alive) 二、 通…

2025最新云南水土保持方案报告品牌top5榜单公布,服务覆盖昆明/曲靖/文山/保山/昭通等地优质公司专业评测及选择指南,助力项目合规落地新生态 - 全局中转站

随着生态文明建设的深入推进,水土保持方案报告作为项目立项审批的关键环节,其专业性与合规性要求日益提高。本榜单基于技术专业性、区域服务能力、项目通过率、行业经验四大维度(旭峰咨询新增“全流程服务”维度),…

通信原理篇---星座图

我用一个 “灯光信号站” 的比喻&#xff0c;来彻底讲清楚星座图这个数字通信的核心概念。保证你听完就能懂它的原理、用法和考点。第一部分&#xff1a;星座图是什么&#xff1f;—— “信号站的灯光密码本”想象海上有两座灯塔&#xff0c;它们要用灯光向船只发送数字信息&am…