python中怎么实现协程(async/await)?

Python中实现协程需使用async def定义协程函数,await暂停执行并交出控制权,事件循环通过asyncio.run()启动,实现单线程内高效并发I/O操作。

python中怎么实现协程(async/await)?

Python中实现协程,核心就是利用

async

await

这两个关键字,它们是

asyncio

库提供的高级抽象。简单来说,协程让程序可以在等待某些操作(比如网络请求、文件读写)完成时,暂停当前任务,去执行其他任务,从而提高效率,尤其是在I/O密集型应用中。它不是真正的并行,而是一种协作式多任务,让你的代码能够“一边等水烧开,一边切菜”。

解决方案

第一次接触

async/await

的时候,可能会觉得有点绕,但一旦你掌握了它的基本模式,就会发现它其实非常直观且强大。实现协程,你主要需要关注以下几点:

定义协程函数: 使用

async def

关键字来定义一个协程函数。这样的函数在被调用时不会立即执行,而是返回一个协程对象。等待协程完成: 在另一个协程函数内部,使用

await

关键字来暂停当前协程的执行,直到

await

后面的协程(或可等待对象,如

asyncio.sleep

)完成。运行事件循环: 协程的执行需要一个事件循环(Event Loop)。

asyncio.run()

是运行顶层协程最简单的方式,它会负责创建和管理事件循环。

下面是一个简单的例子,展示了如何定义和运行协程:

import asyncioimport timeasync def fetch_data(item_id, delay):    """    模拟一个异步的网络请求或数据库查询。    它会等待一段时间,然后返回一些数据。    """    print(f"[{time.strftime('%H:%M:%S')}] 正在为 {item_id} 模拟数据获取,预计等待 {delay} 秒...")    await asyncio.sleep(delay) # 这是一个可等待对象,会暂停当前协程,但不阻塞事件循环    print(f"[{time.strftime('%H:%M:%S')}] {item_id} 数据获取完成。")    return f"数据 for {item_id}"async def process_items():    """    主协程,负责创建并运行多个数据获取任务。    """    print(f"[{time.strftime('%H:%M:%S')}] 开始处理所有项目...")    # 创建多个协程任务,它们将并发运行    task1 = asyncio.create_task(fetch_data("商品A", 3))    task2 = asyncio.create_task(fetch_data("商品B", 1))    task3 = asyncio.create_task(fetch_data("商品C", 2))    # 等待所有任务完成。await 会暂停当前协程,直到所有指定的任务都完成。    # 这里也可以使用 asyncio.gather() 来更优雅地等待多个任务。    result_a = await task1    result_b = await task2    result_c = await task3    print(f"[{time.strftime('%H:%M:%S')}] 所有项目处理完成。")    print(f"获取到的数据: {result_a}, {result_b}, {result_c}")if __name__ == "__main__":    # 运行主协程。asyncio.run() 会自动管理事件循环的启动和关闭。    start_time = time.perf_counter()    asyncio.run(process_items())    end_time = time.perf_counter()    print(f"n总耗时: {end_time - start_time:.2f} 秒")

在这个例子中,

fetch_data

是一个协程函数,它模拟了一个耗时的I/O操作。

process_items

是另一个协程,它通过

asyncio.create_task

创建了三个

fetch_data

任务,并用

await

等待它们完成。你会发现,虽然每个任务都有自己的等待时间,但总的执行时间远小于它们等待时间的总和,因为它们是并发执行的。

立即学习“Python免费学习笔记(深入)”;

async/await 背后的原理是什么?它和多线程有什么区别

很多人刚开始都会把协程和多线程搞混,这很正常。但它们俩的哲学完全不同。要理解

async/await

,核心在于理解事件循环(Event Loop)协作式多任务

当一个协程遇到

await

关键字时,它会暂停自己的执行,将控制权交还给事件循环。事件循环此时会检查是否有其他协程已经准备好运行(比如,之前等待的网络请求已经返回数据了)。如果有,事件循环就会去运行那个准备好的协程。当之前

await

的操作完成(比如数据真的从网络回来了),事件循环会再次调度原先暂停的协程,让它从

await

的地方继续执行。整个过程都在一个单线程中完成。

这和多线程的抢占式多任务形成了鲜明对比:

多线程: 操作系统是老大,它决定什么时候暂停一个线程,什么时候运行另一个线程,线程之间是“抢”CPU资源的。这意味着线程切换(上下文切换)的开销相对较大,而且在Python中,由于全局解释器锁(GIL)的存在,即便在多核CPU上,Python解释器在同一时刻也只能执行一个线程的字节码,这限制了CPU密集型任务的并行度。多线程更像是你雇了几个工人,让他们各自去完成不同的任务,但他们之间可能会抢工具,也需要协调。协程(async/await): 协程是“君子协定”,它们自己决定什么时候暂停,什么时候让出控制权。它们在一个线程里“轮流”执行,没有GIL的限制(因为只有一个线程),上下文切换的开销非常小。协程更像是你一个人,但你学会了高效地切换任务:水烧上了就去切菜,菜切好了就去洗米,水开了就去煮饭。这种方式特别适合I/O密集型任务,因为等待I/O的时间可以用来做其他事情,而不是白白浪费CPU周期。

所以,核心区别在于:多线程是操作系统级别的并行或并发,适用于CPU密集型任务(如果能绕开GIL的话)和I/O密集型任务;而协程是应用程序级别的并发,非常适合I/O密集型任务,因为它能以极低的开销实现大量并发连接,但对CPU密集型任务无能为力(因为仍然是单线程)。

在实际项目中,什么时候应该考虑使用 async/await?

这其实是个很实际的问题,不是所有场景都适合

async/await

。我个人觉得,它最闪耀的舞台,通常是那些需要处理大量并发I/O操作的场景。

你可以这样思考:

当你需要等待外部资源时: 比如网络请求(调用API、爬虫)、数据库查询、文件读写、消息队列的消费与生产等。这些操作通常涉及等待数据从慢速设备(网络、磁盘)传输,等待期间CPU是空闲的。

async/await

能让你的程序在这段空闲时间里去处理其他任务,而不是傻等。例子: 构建高性能的Web服务(如基于FastAPI或Aiohttp)、并发抓取大量网页数据、处理大量实时消息流。当你需要构建响应式、高吞吐量的服务时: 如果你的服务需要同时处理成千上万个客户端连接,并且每个连接都有一定的等待时间(比如聊天服务器、游戏后端),

asyncio

就能发挥巨大作用,因为它能用一个(或少量)线程处理大量并发连接,大大节省了系统资源。例子: WebSocket服务器、长连接服务。当你发现传统的多线程/多进程模型开销太大或管理复杂时: 线程或进程的创建和销毁、上下文切换都有不小的开销,而且多线程的同步(锁、信号量)问题也容易引入bug。协程则轻量得多,且避免了复杂的共享内存同步问题(因为通常在一个线程内)。

什么时候不适合呢?

CPU密集型任务: 如果你的任务主要是进行大量计算,比如图像处理、复杂算法、数据分析中的聚合操作等,

async/await

帮不了你。因为它还是单线程,计算密集型任务会直接阻塞整个事件循环。这种情况下,多进程(

multiprocessing

)才是更好的选择,它可以利用多核CPU进行真正的并行计算。简单的脚本或没有I/O等待的任务: 如果你只是想写个脚本,读取几个本地文件,然后做一些纯粹的计算,那可能

asyncio

反而会把事情搞复杂。引入

async/await

会增加代码的复杂性,如果收益不明显,就没有必要。现有的同步代码库: 如果你大量依赖的第三方库是同步的(比如

requests

库),那么直接在

async

函数里调用它们会阻塞事件循环。虽然有

asyncio.to_thread()

可以把同步阻塞代码放到单独的线程池里运行,但这也增加了额外的复杂性。

总而言之,

async/await

是一个强大的工具,但它有自己的适用范围。在I/O密集型应用中,它能让你事半功倍;但在CPU密集型或简单任务中,它可能只是徒增烦恼。

async/await 使用中常见的坑和调试技巧有哪些?

async/await

的世界里遨游,虽然很爽,但偶尔也会踩到一些“暗礁”。我个人踩过最大的坑,就是在

async def

函数里,不小心调用了一个同步的阻塞I/O函数,结果整个事件循环都被卡住了,服务直接“假死”。

常见的坑:

async

函数中调用阻塞的同步代码: 这是最致命的错误之一。比如在

async def

函数里直接用

time.sleep()

而不是

asyncio.sleep()

,或者使用

requests.get()

而不是

aiohttp

等异步HTTP库。这会直接阻塞事件循环,让你的并发优势荡然无存。解决方法 尽量使用异步版本的库。如果实在需要调用同步阻塞代码,可以考虑使用

await asyncio.to_thread(sync_blocking_function, *args)

,它会在一个单独的线程中运行同步函数,避免阻塞主事件循环。忘记

await

一个协程对象: 当你调用一个

async def

函数时,它返回的是一个协程对象,而不是执行结果。如果你忘记

await

它,这个协程就不会被调度执行,也不会报错(或者只在程序退出时报一个

RuntimeWarning: coroutine '...' was never awaited

)。示例:

my_coroutine()

而不是

await my_coroutine()

asyncio.create_task(my_coroutine())

解决方法: 确保每个协程对象都被

await

了,或者被

asyncio.create_task()

调度成一个任务。不正确地处理任务异常: 如果一个通过

asyncio.create_task()

创建的任务抛出异常,但你没有

await

这个任务,或者没有在

await

时捕获异常,那么这个异常可能不会立即被发现,甚至可能导致整个事件循环崩溃。解决方法: 始终

await

你的任务,并在

await

语句周围使用

try...except

。或者,给任务添加一个

done_callback

来处理异常。

asyncio.gather()

在等待多个任务时,会将所有异常都收集起来。

asyncio.run()

的限制:

asyncio.run()

只能在主线程中调用一次,且不能嵌套。如果你在一个已经运行的事件循环中尝试再次调用它,会报错。解决方法: 如果你已经在事件循环中,并且需要运行另一个协程,可以直接使用

await

asyncio.create_task()

调试技巧:

开启

asyncio

调试模式: 这是我调试

asyncio

代码的首选。你可以通过

python -m asyncio your_script.py

来运行脚本,或者在代码中设置

loop.set_debug(True)

。调试模式会提供更详细的日志输出,比如未被

await

的协程、阻塞事件循环的警告等。

import asyncioasync def main():    # ... your async code ...    passif __name__ == "__main__":    # 获取当前事件循环并设置调试模式    loop = asyncio.get_event_loop()    loop.set_debug(True)    # 运行主协程    loop.run_until_complete(main())    loop.close()

(注意:

asyncio.run()

内部已经包含了事件循环的创建和关闭,如果你用

asyncio.run()

,则无法直接设置

loop.set_debug(True)

。但

asyncio.run()

也可以通过

debug=True

参数来开启调试模式:

asyncio.run(main(), debug=True)

。)

日志记录: 在关键的协程入口和出口添加日志,记录任务的开始、结束、以及任何重要状态。这有助于追踪任务的执行流程和发现异常。

使用

asyncio.all_tasks()

在调试时,你可以用

asyncio.all_tasks()

来查看当前事件循环中所有正在运行或等待的任务,这能帮你了解事件循环的“繁忙”程度和任务状态。

逐步调试器: 虽然调试异步代码比同步代码复杂一些,但像 VS Code 这样的IDE提供的调试器仍然非常有用。学会如何在

await

语句处设置断点,以及如何步入(step into)和步过(step over)协程调用,是提高效率的关键。

理解

asyncio.create_task()

await

的区别:

create_task()

是将一个协程包装成一个

Task

对象,并提交给事件循环,它会立即返回一个

Task

对象,协程会在后台运行。而

await

则是等待一个可等待对象完成,它会暂停当前协程直到被

await

的对象完成。理解这一点能帮你更好地控制任务的生命周期和依赖关系。

调试异步代码确实需要一些耐心和经验,但通过利用这些工具和理解其工作原理,你将能更有效地定位和解决问题。

以上就是python中怎么实现协程(async/await)?的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1372065.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何在低显存GPU上高效运行大型NLP Transformers模型
上一篇 2025年12月14日 12:00:27
python requests库如何发送post请求_python requests库POST请求发送方法
下一篇 2025年12月14日 12:00:31

相关推荐

  • 内存价格大涨15%以上 三星Q3利润远超预期:三年来最高

    10月14日消息,全球存储芯片市场正迎来七年来最繁荣的时期,作为内存与闪存领域的领军企业,三星电子在此轮价格飙升中获益显著,其第三季度运营利润远超市场预期。 三星于今日发布Q3季度初步财报数据,显示该季度营业利润预计达12.1万亿韩元(约合85亿美元),较此前LSEG SmartEstimate对3…

    2026年8月31日
    100
  • 解决”找不到 msvcp140_codecvt_ids.dll”错误的完整方法

    解决”找不到 msvcp140_codecvt_ids.dll”错误的完整方法解决”找不到 msvcp140_codecvt_ids.dll”错误的完整方法解决”找不到 msvcp140_codecvt_ids.dll”错误的完整方法解决”找不到 msvcp140_codecvt_ids.dll”错误的完整方法

    在windows操作系统中,运行部分应用程序或游戏时,许多用户可能会看到“程序无法启动,因为计算机缺少 msvcp140_codecvt_ids.dll”这类提示。该问题通常源于系统未安装或丢失关键的运行库文件。本文将为您全面介绍几种有效的解决方案,帮助您迅速修复该错误,恢复正常运行。 一、借助 D…

    2026年8月31日 用户投稿
    200
  • Java如何高效模拟JavaScript字典结构?

    java高效模拟javascript字典结构 本文探讨如何在Java中高效模拟JavaScript或Python字典的结构,并提供一种基于枚举和Lombok的简洁解决方案。JavaScript字典的典型用法如下: policy = { “expiration”:”2021-02-02t12:00:0…

    2026年8月31日
    000
  • 尊界S800技术发布会召开 带来六大全新技术

    尊界S800技术发布会召开 带来六大全新技术尊界S800技术发布会召开 带来六大全新技术尊界S800技术发布会召开 带来六大全新技术尊界S800技术发布会召开 带来六大全新技术

    鸿蒙智行尊界s800技术发布会:六大智能科技革新出行体验 2月20日,鸿蒙智行尊界品牌盛大发布尊界S800,华为常务董事余承东亲临现场,揭晓了这款车型搭载的六大颠覆性智能技术。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 华为途灵龙行平台…

    2026年8月31日 用户投稿
    100
  • 126邮箱登录入口官网首页 126邮箱登录网页版入口

    126邮箱登录入口官网首页为https://mail.126.com,用户可点击页面左上角“登录”进入,输入含“@126.com”的账号及密码,完成验证后即可登录。 126邮箱登录入口官网首页在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来126邮箱登录网页版入口,需要使用126邮箱进行日…

    2026年8月31日
    000
  • 一款超级经典复古的 Windows 9x 主题风格 Avalonia UI 控件库,满满的回忆杀!

    一款超级经典复古的 Windows 9x 主题风格 Avalonia UI 控件库,满满的回忆杀!一款超级经典复古的 Windows 9x 主题风格 Avalonia UI 控件库,满满的回忆杀!一款超级经典复古的 Windows 9x 主题风格 Avalonia UI 控件库,满满的回忆杀!一款超级经典复古的 Windows 9x 主题风格 Avalonia UI 控件库,满满的回忆杀!

    前言 今天为大家带来一款极具情怀的复古风 windows 9x 主题 avalonia ui 控件库——classic.avalonia,带你一秒穿越回经典的桌面时代,满满的回忆扑面而来。 项目介绍 Classic.Avalonia 是一个基于 .NET 的开源(MIT 许可证)UI 控件库,专为还…

    2026年8月31日 用户投稿
    000
  • 如何解决PHP单元测试中内置函数的模拟问题?使用Composer可以!

    可以通过以下地址学习Composer:学习地址 在进行php单元测试时,模拟内置函数(如time())是一个常见但棘手的问题。直接模拟这些函数不仅复杂,而且可能会受到各种限制和约束。最近在进行一个项目的单元测试时,我遇到了这样的难题:如何在不影响其他测试的前提下,准确地模拟time()函数的返回值?…

    用户投稿 2026年8月31日
    000
  • 电脑丢失dll文件怎么办 教你解决DLL丢失问题

    电脑丢失dll文件怎么办 教你解决DLL丢失问题电脑丢失dll文件怎么办 教你解决DLL丢失问题电脑丢失dll文件怎么办 教你解决DLL丢失问题电脑丢失dll文件怎么办 教你解决DLL丢失问题

    在使用电脑时,不少用户可能遇到这样的问题:运行某个程序时,系统提示“找不到指定的dll文件”或“xxx.dll缺失”。dll文件作为windows系统和各类应用程序正常运行的重要组成部分,一旦丢失或损坏,就可能导致软件无法启动或运行异常。本文将为您详细介绍当电脑提示dll文件丢失时的几种有效解决方法…

    2026年8月31日 用户投稿
    000
  • MySQL安全加密存储数据实践_MySQL敏感字段加密设计

    MySQL安全加密存储数据实践_MySQL敏感字段加密设计MySQL安全加密存储数据实践_MySQL敏感字段加密设计MySQL安全加密存储数据实践_MySQL敏感字段加密设计MySQL安全加密存储数据实践_MySQL敏感字段加密设计

    应用层加密是mysql敏感字段安全存储的核心策略。即数据在写入数据库前由应用加密,读取后由应用解密,确保即使数据库被入侵,攻击者也无法获取明文数据。1. 加密算法首选aes-256 gcm模式,提供强加密和认证功能;2. 初始化向量(iv)必须唯一且随机,与密文一同存储;3. 密钥管理应避免硬编码,…

    2026年8月31日 用户投稿
    100
  • deepseek电脑版怎么下载 DeepSeek电脑安装教程

    deepseek电脑版怎么下载 DeepSeek电脑安装教程deepseek电脑版怎么下载 DeepSeek电脑安装教程deepseek电脑版怎么下载 DeepSeek电脑安装教程deepseek电脑版怎么下载 DeepSeek电脑安装教程

    如何在电脑上安装和使用DeepSeek 截至目前,DeepSeek尚未发布独立的桌面客户端,官方仅提供网页版服务。用户只需访问其官方网站,点击“开始对话”按钮,即可直接使用。 虽然网页版操作便捷,但其运行效果受网络状况和服务器负载影响较大。在网络稳定、服务器空闲时,响应速度快、交互流畅;但一旦遇到高…

    2026年8月31日 用户投稿
    000
  • 笔记本电脑中病毒了一直下软件怎么办

    笔记本电脑中病毒了一直下软件怎么办笔记本电脑中病毒了一直下软件怎么办笔记本电脑中病毒了一直下软件怎么办笔记本电脑中病毒了一直下软件怎么办

    在日常使用笔记本电脑时,如果误下载了来源不明的病毒文件,可能会导致电脑感染病毒,进而出现频繁自动下载软件的问题。那么,当电脑中了病毒并且不停地下载软件时,该如何处理呢?接下来就为大家详细介绍具体的解决办法。 工具/原料: 系统版本:Windows 7系统 设备型号:笔记本电脑 软件版本:小白一键重装…

    2026年8月31日 用户投稿
    000
  • ​​微信文件传输失败?解决文件发送问题​​

    微信文件传不出去时,首先检查网络连接是否稳定,尝试切换wi-fi或数据流量;2. 重启微信应用,彻底关闭后重新打开以清除临时错误;3. 清理微信缓存,进入“设置-通用-存储空间”进行缓存清理;4. 更新微信至最新版本,避免因旧版本bug导致传输失败;5. 检查文件是否损坏或格式异常,尝试发送其他文件…

    2026年8月31日
    100
  • 配置漏洞之DNS域传送

    dns域传送漏洞是一种常见的dns安全问题,本文将详细介绍其利用方式、漏洞原因以及修复方法。 Windows利用方式: 使用nslookup工具: nslookupserver=ns.vul.comls vul.com Linux利用方式: 使用dig工具: dig axfr @ns.vul.com…

    2026年8月31日
    000
  • 0x80073701错误代码怎么解决 简单几步教会你

    0x80073701错误代码怎么解决 简单几步教会你0x80073701错误代码怎么解决 简单几步教会你0x80073701错误代码怎么解决 简单几步教会你0x80073701错误代码怎么解决 简单几步教会你

    windows更新过程中出现错误代码“0x80073701”是许多用户常遇到的问题。该错误通常会导致系统无法成功安装更新补丁,尤其是在尝试安装功能更新或累积更新时更为明显。此问题的根本原因多为系统关键文件丢失或windows更新组件损坏。本文将深入解析“0x80073701”错误的来源,并提供多种高…

    2026年8月31日 用户投稿
    000
  • Java如何优雅地实现类似JavaScript或Python字典的结构?

    java优雅实现类似javascript或python字典结构 本文探讨如何在Java中优雅地实现类似于JavaScript或Python字典的结构,即使用键值对存储数据。 直接使用Java基本数据类型无法达到Python或JavaScript字典的简洁性,因此需要借助其他方式。 一种有效的方法是结…

    2026年8月31日
    000
  • 微信小程序在华为鸿蒙4.0系统上定位失败是什么原因?

    微信小程序在华为鸿蒙4.0系统上的定位异常问题分析 部分开发者反映,其微信小程序在华为鸿蒙4.0系统上出现定位功能异常:定位成功率低,甚至无法启动定位。然而,在iOS系统上运行正常。本文将对此问题进行分析,并提供解决方案。 开发者提供的代码使用了uni.getLocation()方法进行定位,该方法…

    2026年8月31日
    000
  • 照片RAW文件占用空间太多怎么办_照片RAW文件占用空间太多如何转换格式详细方法

    优先转换或删除无用RAW文件以节省空间,可将其转为DNG格式压缩存储,或导出为JPEG/TIFF用于分享,结合云存储与本地精简策略管理,按年份分类备份至硬盘,电脑仅保留轻量版本,实现画质与空间平衡。 RAW文件虽然能保留照片最多的细节信息,适合专业后期处理,但确实体积大、占用存储空间多。如果你不需要…

    2026年8月31日
    000
  • 电脑出现”无法启动此程序,因为计算机中丢失dll“的快速修复方法

    电脑出现”无法启动此程序,因为计算机中丢失dll“的快速修复方法电脑出现”无法启动此程序,因为计算机中丢失dll“的快速修复方法电脑出现”无法启动此程序,因为计算机中丢失dll“的快速修复方法电脑出现”无法启动此程序,因为计算机中丢失dll“的快速修复方法

    在日常使用电脑过程中,不少用户在启动某些应用程序或游戏时,可能会遇到弹窗提示:“由于计算机中缺少某某dll文件,无法运行此程序”。这类问题在windows系统中十分常见。本文将为你提供6种高效解决dll丢失问题的方法。 方法1:重新安装出错的软件 有时DLL缺失是由于安装包不完整或版本不兼容所致。建…

    2026年8月31日 用户投稿
    000
  • ECharts图表无法充满父容器?百分百宽高设置失效的原因及解决方案

    ECharts图表无法充满父容器的排查与解决 在使用ECharts创建图表时,经常会遇到图表无法完全填充父容器的情况。本文将分析一个典型案例,并提供有效的解决方案。 案例中,开发者使用了width: 100%; height: 100%;样式设置图表容器及其父容器的尺寸,期望图表自适应容器大小。然而…

    2026年8月31日
    000
  • 如何使用Composer解决WordPress多语言管理问题?wp-cli/language-command助你轻松搞定!

    可以通过一下地址学习composer:学习地址 在管理 wordpress 网站时,处理多语言支持常常是一个挑战。最近,我在维护一个多语言网站时遇到了一个问题:如何高效地安装、激活和管理不同语言包?手动操作不仅耗时,而且容易出错。幸运的是,通过使用 composer 和 wp-cli/languag…

    用户投稿 2026年8月31日
    000

发表回复

登录后才能评论
关注微信