目录
-
- [Python 异步编程深度解析:从生成器到 Asyncio 的演进之路](#Python 异步编程深度解析:从生成器到 Asyncio 的演进之路)
- 第一章:阻塞的代价与异步的曙光
- [第二章:生成器的伪装------Python 3.4 时代的异步魔法](#第二章:生成器的伪装——Python 3.4 时代的异步魔法)
-
- [2.1 yield 的双重身份](#2.1 yield 的双重身份)
- [2.2 PEP 3156 与 "Future" 的幻想](#2.2 PEP 3156 与 "Future" 的幻想)
- [第三章:async/await 的诞生------原生异步的标准化](#第三章:async/await 的诞生——原生异步的标准化)
-
- [3.1 关键字的语义分离](#3.1 关键字的语义分离)
- [3.2 事件循环(Event Loop)的进化](#3.2 事件循环(Event Loop)的进化)
- [第四章:实战对比------同步 vs 异步的性能博弈](#第四章:实战对比——同步 vs 异步的性能博弈)
-
- [4.1 同步方案(Requests)](#4.1 同步方案(Requests))
- [4.2 异步方案(Aiohttp + Asyncio)](#4.2 异步方案(Aiohttp + Asyncio))
- 第五章:进阶技巧------生成器在异步迭代中的回归
-
- [5.1 异步生成器语法](#5.1 异步生成器语法)
- [5.2 为什么这很重要?](#5.2 为什么这很重要?)
- 第六章:避坑指南与最佳实践
- 结语:异步是趋势,但不是银弹
专栏导读
🌸 欢迎来到Python办公自动化专栏---Python处理办公问题,解放您的双手
🏳️🌈 个人博客主页:请点击------> 个人的博客主页 求收藏
🏳️🌈 Github主页:请点击------> Github主页 求Star⭐
🏳️🌈 知乎主页:请点击------> 知乎主页 求关注
🏳️🌈 CSDN博客主页:请点击------> CSDN的博客主页 求关注
👍 该系列文章专栏:请点击------>Python办公自动化专栏 求订阅
🕷 此外还有爬虫专栏:请点击------>Python爬虫基础专栏 求订阅
📕 此外还有python基础专栏:请点击------>Python基础学习专栏 求订阅
文章作者技术和水平有限,如果文中出现错误,希望大家能指正🙏
❤️ 欢迎各位佬关注! ❤️
Python 异步编程深度解析:从生成器到 Asyncio 的演进之路
第一章:阻塞的代价与异步的曙光
在 Python 的开发生态中,我们经常面临一个看似无解的困境:同步代码的简洁性 与 高并发场景的性能需求 之间的矛盾。
想象一个典型的 Web 服务场景:你的应用需要请求三个不同的第三方 API,每个请求耗时 200 毫秒。在传统的同步模型(Synchronous I/O)下,代码会像流水线一样依次执行:请求 A -> 等待 -> 请求 B -> 等待 -> 请求 C。总耗时将是 200ms * 3 = 600ms。这期间,CPU 只能干瞪眼,处于"阻塞"状态,无法处理其他任务。
这就是同步编程在 I/O 密集型任务(I/O-bound)中的最大痛点:大量的时间浪费在等待上。
异步编程(Asynchronous Programming)的出现,就是为了打破这种"等待"的魔咒。它的核心理念是:在等待某个耗时操作(如网络请求、文件读写)完成时,不要傻等,去干点别的事情。
在 Python 的世界里,这场变革并非一蹴而就,而是经历了一场漫长的演进。从早期的 Twisted 框架,到官方引入的 asyncio 库,再到 async/await 语法的普及,Python 社区一直在寻找一种既高性能又能保持代码可读性的解决方案。而在这场演进中,生成器(Generator) 扮演了至关重要的"前传"角色。
第二章:生成器的伪装------Python 3.4 时代的异步魔法
在 Python 3.5 正式引入 async/await 之前,异步编程主要依赖一个看似与并发无关的特性:生成器(Generator)。
2.1 yield 的双重身份
普通的函数一旦执行 return 就会彻底结束,而生成器函数使用 yield 关键字,可以在函数执行过程中暂停并保存状态,下次调用时再从暂停的地方继续。
python
def simple_generator():
yield 1
yield 2
yield 3
gen = simple_generator()
print(next(gen)) # 输出 1
print(next(gen)) # 输出 2
2.2 PEP 3156 与 "Future" 的幻想
在 Python 3.4 的 asyncio 库中,开发者利用生成器的特性,配合 @asyncio.coroutine 装饰器和 yield from 语法,模拟出了类似异步的效果。
当时的代码风格大致如下:
python
import asyncio
@asyncio.coroutine
def old_style_async():
print("Start")
# yield from 会把子生成器的值传递出去,同时挂起当前函数
yield from asyncio.sleep(1)
print("End")
loop = asyncio.get_event_loop()
loop.run_until_complete(old_style_async())
在这个阶段,yield from asyncio.sleep(1) 实际上是在向事件循环(Event Loop)发送一个信号:"我需要等待 1 秒,你可以去运行别的任务了"。当 sleep 结束后,事件循环会把结果送回这个生成器,函数继续执行。
这种写法的局限性:
- 视觉混淆:生成器通常用于生成数据流,而在这里被"滥用"于控制流程,代码阅读者容易困惑。
- 类型推断困难:IDE 和静态分析工具很难区分一个生成器是用于迭代数据,还是用于异步等待。
- 嵌套地狱 :虽然
yield from解决了部分嵌套问题,但代码结构依然不够直观。
这就是 Python 异步开发的"青铜时代":功能强大,但写法别扭,充满了"魔法"。
第三章:async/await 的诞生------原生异步的标准化
为了解决上述混乱,PEP 492 引入了专门的语法糖,将异步编程从"伪装成生成器"中解放出来,这就是 Python 3.5+ 的 async/await。
3.1 关键字的语义分离
async def:声明这是一个原生协程(Native Coroutine),而不是一个普通生成器。await:专门用于挂起协程,等待结果。它替代了以前的yield from。
这种区分带来了巨大的好处:代码即文档。
python
import asyncio
async def new_style_async():
print("Start")
# 明确的语义:等待 sleep 完成
await asyncio.sleep(1)
print("End")
当你看到 async def,你就知道这里面有异步操作;当你看到 await,你就知道这里会发生非阻塞的等待。
3.2 事件循环(Event Loop)的进化
async/await 只是语法层面的改变,真正的幕后英雄依然是 事件循环。
在 Python 3.7 之前,管理事件循环非常繁琐,需要手动获取、运行、关闭。Python 3.7 引入了 asyncio.run(),极大地简化了入口代码:
python
async def main():
await asyncio.sleep(1)
if __name__ == "__main__":
asyncio.run(main()) # 一行代码搞定事件循环的生命周期
这一阶段的异步编程,终于具备了大规模推广的基础。代码清晰、逻辑直观,且性能卓越。
第四章:实战对比------同步 vs 异步的性能博弈
为了直观展示异步开发的威力,我们来看一个 HTTP 请求 的实战案例。假设我们需要请求一个延迟为 1 秒的接口 10 次。
4.1 同步方案(Requests)
使用最流行的 requests 库,代码简单,但效率极低。
python
import time
import requests
def sync_download(url):
# 总耗时 = 10 * 1秒 = 10秒
for i in range(10):
requests.get(url)
print(f"Sync task {i} done")
start = time.time()
sync_download("http://httpbin.org/delay/1")
print(f"同步耗时: {time.time() - start:.2f}s")
结果 :约 10.5秒。CPU 在这 10 秒内几乎完全闲置。
4.2 异步方案(Aiohttp + Asyncio)
使用异步 HTTP 客户端 aiohttp 配合 asyncio。
python
import time
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def async_download(url):
tasks = []
async with aiohttp.ClientSession() as session:
# 创建 10 个任务,但不要立即 await
for i in range(10):
task = asyncio.create_task(fetch(session, url))
tasks.append(task)
# gather 会并发运行所有任务
await asyncio.gather(*tasks)
print("All async tasks done")
start = time.time()
asyncio.run(async_download("http://httpbin.org/delay/1"))
print(f"异步耗时: {time.time() - start:.2f}s")
结果 :约 1.2秒。
分析:
- 并发(Concurrency):异步并非并行(Parallelism,利用多核),但在 I/O 等待上,它通过事件循环调度,让 10 个请求"同时"发出去并等待。
- 时间缩减 :性能提升了近 8-9 倍。这在爬虫、微服务网关、数据库连接池等场景中,是质的飞跃。
第五章:进阶技巧------生成器在异步迭代中的回归
虽然生成器在异步控制流中退场了,但在 数据流处理 中,它以 async generator 的形式强势回归。
在处理大量数据(如流式读取大文件、实时日志分析)时,我们不希望一次性把所有数据加载到内存。这时,异步生成器就派上了用场。
5.1 异步生成器语法
定义:async def + yield + async for。
python
import asyncio
async def async_number_generator():
for i in range(5):
await asyncio.sleep(0.1) # 模拟数据生成的延迟
yield i
async def process_data():
# 异步迭代
async for num in async_number_generator():
print(f"Processing {num}")
asyncio.run(process_data())
5.2 为什么这很重要?
在迭代开发中,我们经常需要处理"生产者-消费者"模型。
- 普通生成器:生产数据的速度是同步的,如果生产一个数据很慢(涉及 I/O),整个迭代器都会阻塞。
- 异步生成器 :生产数据的过程本身可以是异步的(
await某个资源),消费者可以一边等待生产,一边处理已有的数据。
这在现代 Web 框架(如 FastAPI)中非常常见,用于 流式响应(Streaming Response),能够显著降低服务器内存占用,提升首字节到达时间(TTFB)。
第六章:避坑指南与最佳实践
异步编程虽然强大,但也是"双刃剑"。以下是开发中必须注意的陷阱:
-
阻塞即毒药 :
在
async def函数中,绝对不能 使用阻塞库(如time.sleep()、requests、MySQLdb)。- 错误做法 :
time.sleep(1)------ 会冻结整个事件循环,所有并发任务都会卡死。 - 正确做法 :
await asyncio.sleep(1)------ 让出控制权。
- 错误做法 :
-
不要遗漏 await :
忘记写
await是新手最常见的错误。这会导致返回一个协程对象,而不是预期的结果,且通常不会立即报错,导致极难调试的逻辑错误。 -
混合使用 :
如果你的应用既有 CPU 密集型任务(如复杂的图像处理、数学计算),又有 I/O 密集型任务,单纯的
asyncio可能不够用。因为事件循环会被 CPU 任务占满。- 解决方案 :使用
loop.run_in_executor将 CPU 任务扔到线程池中执行,避免阻塞主事件循环。
- 解决方案 :使用
结语:异步是趋势,但不是银弹
从 yield 的伪装到 async/await 的标准化,Python 异步编程的演进反映了我们对高并发、高性能的永恒追求。
总结观点:
- 生成器 是 Python 异步机制的基石,理解它有助于理解协程的本质。
- Asyncio 是处理 I/O 密集型任务的利器,能将吞吐量提升一个数量级。
- 并非所有场景都适合异步:如果你的业务主要是 CPU 密集型,或者业务逻辑极其简单,引入异步带来的复杂度可能超过收益。
在当下的迭代开发中,掌握 async/await 已经不再是可选项,而是 Python 高级开发者的必备技能。它让你在面对高并发挑战时,拥有了从容调度系统资源的底气。
互动话题 :
你在项目中遇到过最棘手的异步编程问题是什么?是回调地狱的遗留代码,还是难以排查的阻塞调用?欢迎在评论区分享你的经历!
结尾
希望对初学者有帮助;致力于办公自动化的小小程序员一枚
希望能得到大家的【❤️一个免费关注❤️】感谢!
求个 🤞 关注 🤞 +❤️ 喜欢 ❤️ +👍 收藏 👍
此外还有办公自动化专栏,欢迎大家订阅:Python办公自动化专栏
此外还有爬虫专栏,欢迎大家订阅:Python爬虫基础专栏
此外还有Python基础专栏,欢迎大家订阅:Python基础学习专栏