news 2026/5/23 12:59:17

Day 39:【99天精通Python】异步编程 (AsyncIO) 上篇 - 协程的魔法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day 39:【99天精通Python】异步编程 (AsyncIO) 上篇 - 协程的魔法

Day 39:【99天精通Python】异步编程 (AsyncIO) 上篇 - 协程的魔法

前言

欢迎来到第39天!

在前面的课程中,我们学习了多线程。线程虽然好用,但它是由操作系统负责调度的。操作系统很忙,它要在几千个线程之间来回切换(Context Switch),这需要消耗不少资源。当并发量达到上万级别时,线程切换的开销就会拖垮系统。

协程 (Coroutine)是一种比线程更轻量级的存在。

  • 线程:操作系统决定什么时候切换,你无法控制。
  • 协程程序自己决定什么时候切换(“我等数据的时候,你先干别的”)。

Python 3.4 引入了asyncio库,Python 3.5 引入了asyncawait关键字,标志着 Python 进入了原生异步编程时代。这是高性能网络服务器(如 FastAPI, Tornado)的基石。

本节内容:

  • 同步 (Sync) vs 异步 (Async)
  • asyncawait关键字
  • 事件循环 (Event Loop)
  • 运行协程:asyncio.run()
  • 并发执行:asyncio.gather()
  • 实战:体验"光速"睡眠

一、同步 vs 异步

1.1 同步 (Synchronous)

代码从上到下依次执行。如果第一行卡住了(比如下载文件),第二行就得干等。

importtimedeftask(name):time.sleep(1)# 阻塞 1 秒print(f"{name}完成")task("A")task("B")# 总耗时: 2 秒

1.2 异步 (Asynchronous)

当第一行卡住(等待 I/O)时,程序会自动挂起它,去执行第二行。等第一行结果回来了,再恢复执行。

# 伪代码逻辑awaittask("A")# 你先下着,我去干别的awaittask("B")# 你也下着# 总耗时: 约 1 秒 (因为是同时等的)

二、Hello AsyncIO

2.1 定义协程 (async def)

使用async def定义的函数不再是普通函数,而是一个协程函数。调用它不会立即执行,而是返回一个协程对象。

importasyncioasyncdefsay_hello():print("Hello")return"World"# 直接调用不会执行打印!# coroutine = say_hello()# print(coroutine) # <coroutine object ...>

2.2 运行协程 (asyncio.run)

要让协程跑起来,必须把它扔进事件循环 (Event Loop)
Python 3.7+ 提供了最简单的入口:asyncio.run()

importasyncioasyncdefmain():print("开始")# await 后面必须跟一个可等待对象 (Coroutine, Task, Future)# 这里不能用 time.sleep,要用 asyncio.sleepawaitasyncio.sleep(1)print("结束")if__name__=='__main__':asyncio.run(main())

注意asyncio.sleep(1)是非阻塞的睡眠,而time.sleep(1)是阻塞的。在协程中千万别用time.sleep,否则整个程序都会卡死!


三、并发执行:asyncio.gather

如果我们按顺序写两个await,它们还是串行的。

asyncdefmain():awaittask(1)# 等它做完awaittask(2)# 再做这个# 依然是串行,没体现出异步优势

要实现并发,我们需要告诉事件循环:“把这几个任务一起安排了!”。使用asyncio.gather()

实战对比:同步 vs 异步

我们模拟烤面包(2秒)和煮咖啡(3秒)。

importasyncioimporttime# --- 异步任务 ---asyncdefmake_toast():print("开始烤面包...")awaitasyncio.sleep(2)# 模拟耗时 I/Oprint("面包烤好了!")return"Toast"asyncdefmake_coffee():print("开始煮咖啡...")awaitasyncio.sleep(3)print("咖啡煮好了!")return"Coffee"asyncdefmain():start=time.time()print("--- 早餐开始 ---")# 并发执行两个任务# gather 会等待所有任务完成,并按顺序返回结果列表results=awaitasyncio.gather(make_toast(),make_coffee())end=time.time()print(f"--- 早餐结束,耗时:{end-start:.2f}秒 ---")print(f"结果:{results}")if__name__=='__main__':asyncio.run(main())

运行结果

--- 早餐开始 --- 开始烤面包... 开始煮咖啡... (过了2秒) 面包烤好了! (又过1秒) 咖啡煮好了! --- 早餐结束,耗时: 3.01 秒 --- 结果: ['Toast', 'Coffee']

如果用同步方式,需要 2+3=5 秒。异步方式只用了 3 秒(取决于最长的那个任务)。


四、深入理解:await 到底在干嘛?

await关键字的作用是:

  1. 挂起当前协程(暂停执行)。
  2. 交出控制权给事件循环,让它去调度其他协程。
  3. 等待后面的对象(如sleep或网络请求)返回结果。
  4. 恢复执行。

这就像你在餐厅点菜:

  • await 点菜():你告诉服务员要什么,服务员去厨房下单(交出控制权)。
  • 服务员去服务其他桌的客人(调度其他任务)。
  • 厨房做好了(IO完成),服务员把菜端给你(恢复执行)。

五、常见的坑 (必看)

坑1:在协程里写了阻塞代码

这是新手最容易犯的错。

asyncdefbad_coroutine():print("开始")# time.sleep 是阻塞的!它会霸占 CPU,不让出控制权。# 导致整个线程卡住,其他协程也跑不了。importtime time.sleep(5)print("结束")

原则:在async def函数里,所有耗时操作都必须是异步的(支持await的),比如asyncio.sleep,或者异步库(aiohttp,aiomysql)。不能用普通的requests,time.sleep

坑2:忘记写 await

asyncdefmain():# 这样写只会创建一个协程对象,但不会执行它!# RuntimeWarning: coroutine 'xxx' was never awaitedasyncio.sleep(1)# 正确写法awaitasyncio.sleep(1)

六、小结

异步编程 AsyncIO

核心概念

关键字

运行模式

Event Loop (调度中心)

Coroutine (协程对象)

非阻塞 I/O

async def (定义)

await (挂起/等待)

asyncio.run() (入口)

asyncio.gather() (并发)

关键要点

  1. 协程是单线程并发,靠的是"合作式调度"(自己主动让出 CPU)。
  2. async def定义协程,await调度协程。
  3. asyncio.gather是并发执行的神器。
  4. 千万别在协程里用阻塞代码(如time.sleep,requests),否则一核有难,八核围观。

七、课后作业

  1. 异步倒计时:编写一个协程countdown(name, n),每秒打印一次倒计时(n, n-1, … 1)。并发运行 3 个倒计时任务(比如 "A"倒数3秒,"B"倒数5秒)。
  2. 效率对比:编写一个普通的函数sync_cal()(使用time.sleep(1))和一个协程async_cal()(使用asyncio.sleep(1))。分别循环调用它们 5 次(同步循环 vsgather并发),对比总耗时。
  3. 思考题:为什么计算密集型任务(如算圆周率)不适合用asyncio?(提示:回顾一下 GIL 和单线程的本质)。

下节预告

Day 40:异步编程 (AsyncIO) 下篇 - aiohttp- 既然不能用requests,那在协程里怎么发网络请求?我们将学习 Python 最强的异步网络库aiohttp,体验每秒几千次请求的快感!


系列导航

  • 上一篇:Day 38 - 线程池与进程池
  • 下一篇:Day 40 - 异步编程AsyncIO下(待更新)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/22 14:40:46

基于STSW-LINK007工具的STLink升级实战案例

STLink固件升级实战&#xff1a;从避坑到精通的完整指南 在嵌入式开发的世界里&#xff0c;调试器就像医生的听诊器——看不见它的时候风平浪静&#xff0c;一旦“连不上”、“下不进程序”&#xff0c;整个项目进度立刻停摆。而当你发现原因竟是 调试器固件太老不支持新芯片…

作者头像 李华
网站建设 2026/5/22 9:31:50

大语言模型完整技术栈:从理论到实践的全面指南

本文全面介绍了大语言模型(LLM)的核心技术&#xff0c;包括Transformer架构、预训练挑战、分布式训练方法。重点讲解了参数高效微调(PEFT)技术如LoRA和QLoRA&#xff0c;以及提示工程策略。同时详细探讨了模型优化技术&#xff0c;包括量化(GPTQ、NF4、GGML)、蒸馏和剪枝&#…

作者头像 李华
网站建设 2026/5/20 13:03:48

项目一多就混乱?试试把大目标拆成7层小动作

我见过太多这样的现场&#xff1a;每天早会一开&#xff0c;大家低头刷手机&#xff0c;汇报永远是“差不多完成了”、“快了快了”&#xff1b;群里消息满天飞&#xff0c;每个人都在跟进&#xff0c;但项目依旧卡在原地&#xff1b;老板问一句&#xff1a;“现在到底卡在哪&a…

作者头像 李华
网站建设 2026/5/21 10:57:27

国产力量崛起:2026十大本土HR软件厂商深度盘点

在信息技术应用创新和供应链安全的国家战略指引下&#xff0c;中国人力资源软件市场正经历一场深刻的国产化替代浪潮。曾经由SAP、Oracle等国际巨头主导的HR系统市场&#xff0c;如今已涌现出一批技术领先、服务扎实的国产优秀厂商。这些本土企业不仅深谙中国企业管理特色&…

作者头像 李华