news 2026/9/18 20:29:02

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/9/3 1:31:42

基于Simulink的混合PO与INC切换MPPT策略仿真

目录 手把手教你学Simulink 一、引言&#xff1a;为什么需要“混合P&O与INC”&#xff1f; 二、系统整体架构 控制流程&#xff1a; 三、核心算法详解 1. P&O 算法&#xff08;扰动观察法&#xff09; 2. INC 算法&#xff08;电导增量法&#xff09; 3. 光照突…

作者头像 李华
网站建设 2026/9/5 11:11:26

工业场景中弧形导轨的安装要点

弧形导轨作为工业自动化中实现弧线运动的核心部件&#xff0c;常用于机械臂关节、旋转工作台、自动化生产线转弯部位&#xff0c;医疗CT机的旋转扫描部件也依赖高精度弧形导轨实现平滑运动。其安装质量直接影响设备运行精度与寿命&#xff0c;从材料准备到定位调试&#xff0c;…

作者头像 李华
网站建设 2026/9/14 1:54:39

2026年你应该掌握的进阶版 Gemini CLI 实用指南

现在用AI&#xff0c;只会在网页版当聊天机器人用吗&#xff1f;那你就out了。 Gemini CLI 是 Google 推出的终端 AI 助手。相比于网页版&#xff0c;命令行工具在处理本地文件、读取项目上下文方面有着天然优势。对于开发者而言&#xff0c;它不仅仅是一个聊天机器人&#xf…

作者头像 李华
网站建设 2026/9/15 9:36:17

ppo 找出口模型 训练 笔记 26/1/13

cnn模型大小我给他控制在训练耗时30s左右&#xff08;4060ti&#xff09; 动作空间6个&#xff1a;4个移动2个转头&#xff0c;因为一开始都要跑一遍&#xff0c;动作太多需要跑更多步才能吃到正反馈 我现在设置是60步一episode&#xff0c;5 episode训练一次 转头70度左右&…

作者头像 李华
网站建设 2026/9/13 5:34:18

深度学习计算机毕设之基于python-AI机器学习对狗表情训练识别基于python-AI深度学习对狗表情训练识别

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

作者头像 李华