news 2026/4/16 9:04:13

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/4/9 15:21:24

大模型本地化部署与微调实战指南:从入门到精通

文章探讨了大模型在垂直领域的应用挑战&#xff0c;包括幻觉和时效性问题&#xff0c;提出了检索增强生成(RAG)和微调(Fine-tuning)两种解决方案。详细介绍了微调的种类、工具框架及费用估算方法&#xff0c;以及实际操作流程。通过这些技术手段&#xff0c;可以增强大模型在特…

作者头像 李华
网站建设 2026/3/27 22:11:32

AI智能体(Agent)全解析+代码示例

本文全面介绍了AI智能体(Agent)的原理与实现&#xff0c;包括智能体的定义、特征与传统LLM应用的区别&#xff0c;以及构建智能体的三要素(模型、工具、指令)。文章详细讲解了智能体的编排模式(单智能体/多智能体系统)和护栏体系&#xff0c;并通过LangGraph框架提供了最小可运…

作者头像 李华
网站建设 2026/4/8 20:32:34

深度学习毕设项目推荐-基于python_CNN深度学习卷积神经网络对猫狗识别

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

作者头像 李华
网站建设 2026/4/15 15:50:16

【毕业设计】基于python_CNN深度学习卷积神经网络训练识别猫的表情

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

作者头像 李华
网站建设 2026/4/1 15:12:04

【1、Gis数据-河南省四大流域一二级流域树划分】

1.河南省树结构展示&#xff1a; 2.河南省流域树结构数据表存储详情&#xff1a; W 全流域 一级流域&#xff1a; WC 海河流域 WD 黄河流域 WE 淮河流域 WF 长江流域 二级流域&#xff1a; WCF 漳卫南运河水系 WCG 徒骇、马颊河水系 WDA 黄河主干水系 WDA 黄河干流水系 WDA 金…

作者头像 李华