asyncio 用「协程 + 事件循环」实现单线程并发:在 IO 等待时让出控制权去执行别的协程,等待结束后再恢复。适合 IO 密集型任务,不适合 CPU 密集型。
核心抽象
| 概念 | 作用 |
|---|---|
| 协程(Coroutine) | async def 定义;遇到 await 暂停,让出执行权 |
| 事件循环(Event Loop) | 调度所有协程,由 asyncio.run 启动 |
| 任务(Task) | 包装协程提交给事件循环;用 asyncio.create_task() 创建 |
同步 vs 异步
# 同步:3 个任务依次执行,总耗时 3s
import time
def task(name):
print(f"开始 {name}")
time.sleep(1)
print(f"结束 {name}")
start = time.time()
task("任务1"); task("任务2"); task("任务3")
print(f"总耗时: {time.time() - start:.2f}s")# 异步:3 个任务并发,总耗时 ≈ 1s
import asyncio, time
async def task(name):
print(f"开始 {name}")
await asyncio.sleep(1) # 让出执行权
print(f"结束 {name}")
async def main():
start = time.time()
await asyncio.gather(task("任务1"), task("任务2"), task("任务3"))
print(f"总耗时: {time.time() - start:.2f}s")
asyncio.run(main())关键差异:time.sleep 阻塞线程,await asyncio.sleep 让出协程,事件循环立即切去运行其他协程。
Task 与 gather
Task — 把协程提交给事件循环并行运行:
async def main():
t1 = asyncio.create_task(task("任务1"))
t2 = asyncio.create_task(task("任务2"))
await t1
await t2gather — 并发执行多个协程并按顺序收集返回值:
async def task(name):
await asyncio.sleep(1)
return f"{name} 完成"
async def main():
results = await asyncio.gather(task("任务1"), task("任务2"))
print(results) # ['任务1 完成', '任务2 完成']适用与不适用
| 场景类型 | 推荐方案 | 说明 |
|---|---|---|
| IO 密集型(网络、磁盘、DB) | asyncio + aiohttp/asyncpg/aiofiles | 等待时不浪费 CPU |
| CPU 密集型(计算、图像处理) | multiprocessing | asyncio 单线程,CPU 忙时无法切换 |
三个易踩的坑
- 在协程里调用
time.sleep()等阻塞函数会阻塞整个事件循环,必须用异步替代 - 协程对象只是配方,没有
await或create_task不会执行 asyncio.run()不能在已运行的事件循环里调用(如 Jupyter 中改用await)