#技术
Python 异步编程入门:从回调到 async/await
厨房比喻
想象你在做早餐:
- 你把面包放进烤面包机(启动一个任务)
- 不用站在那等面包烤好,你转身去煎鸡蛋(切换到另一个任务)
- 面包机"叮"一声(任务完成通知)
- 你回来取面包(获取结果)
这就是异步编程的核心思想——在等待的时候做别的事。
同步 vs 异步
先看同步版本的"做早餐":
import time
def make_toast():
print("面包放进烤面包机...")
time.sleep(3) # 模拟等待 3 秒
print("面包烤好了!")
return "Toast"
def fry_eggs():
print("开始煎鸡蛋...")
time.sleep(5) # 模拟等待 5 秒
print("鸡蛋煎好了!")
return "Eggs"
# 同步执行:总共 8 秒
toast = make_toast()
eggs = fry_eggs()
再看异步版本:
import asyncio
async def make_toast():
print("面包放进烤面包机...")
await asyncio.sleep(3) # 不阻塞,让出控制权
print("面包烤好了!")
return "Toast"
async def fry_eggs():
print("开始煎鸡蛋...")
await asyncio.sleep(5)
print("鸡蛋煎好了!")
return "Eggs"
async def make_breakfast():
# 并发执行:总共 5 秒(取最长)
toast, eggs = await asyncio.gather(
make_toast(),
fry_eggs()
)
print(f"早餐完成: {toast} + {eggs}")
asyncio.run(make_breakfast())
关键概念
| 概念 | 厨房比喻 | 代码 |
|---|---|---|
| 协程 (coroutine) | 你做一道菜的过程 | async def 函数 |
| 事件循环 (event loop) | 你在厨房里来回走动,检查各个任务 | asyncio.run() |
| await | 等面包机"叮"一下 | await 关键字 |
| gather | 同时做多件事 | asyncio.gather() |
什么时候用 async
- 大量 I/O 操作:网络请求、文件读写、数据库查询
- 需要并发但不想要多线程的复杂性
- Web 服务器处理并发请求
不适合 CPU 密集型计算——那需要用多进程。
一个实际例子
import asyncio
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def fetch_all(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
return await asyncio.gather(*tasks)
# 同时请求 10 个 URL,总共只要最长那个的时间
urls = [f"https://api.example.com/item/{i}" for i in range(10)]
results = asyncio.run(fetch_all(urls))
总结
协程不难,难的是忘掉"顺序执行"的思维习惯。下次写 I/O 密集的 Python 代码时,试试 async/await。