深入浅出 Python asyncio:从原理到实战的非阻塞 I/O 指南
在高并发和大规模网络请求场景下,传统同步阻塞 I/O 往往成为性能瓶颈。Python 的 asyncio 模块应运而生,它通过事件循环与协程,让 I/O 操作“非阻塞”执行,极大提升并发效率。本篇文章将从原理到实战,结合丰富的代码示例,帮你彻底搞懂 asyncio 的运行机制与应用场景。
一、引言:为什么需要 asyncio?
在 Web 服务、爬虫、即时通讯、微服务等场景中,我们常常面对成千上万并发 I/O 请求。传统的多线程/多进程模型:
- 线程切换开销大、上下文切换频繁
- 进程资源消耗高、启动慢
而 asyncio 利用单线程事件循环,通过协程 (coroutine) 把 I/O 操作前后的等待时间“挂起”,在此期间让出 CPU 给其他任务执行,实现“高效并发”与“低内存占用”的完美平衡。
二、同步阻塞 vs 异步非阻塞
-
同步阻塞
- I/O 请求发出后,线程会阻塞等待结果返回
- 易于理解,调试简单,但并发能力差
-
异步非阻塞
- 发起 I/O 操作后立即返回,将后续操作挂在回调/协程中
- CPU 在等待期间可执行其他任务,资源利用率更高
asyncio 就是为了让 Python 在单线程下优雅地实现异步非阻塞 I/O。
三、asyncio 核心概念
-
事件循环(Event Loop)
核心调度器,负责注册、监听、分发 I/O 事件,并驱动协程执行。 -
协程(Coroutine)
基于async def
定义的函数,遇到await
时挂起,等待 I/O 就绪后自动恢复。 -
任务(Task)
将协程包装为可由事件循环调度的对象,通过asyncio.create_task()
或loop.create_task()
创建。 -
Future
表示将来可能获得的某个结果,和 Task 密切相关,通常由 Task 自动创建。
四、事件循环实战
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
# 方式一:Python 3.7+
asyncio.run(say_hello())
# 方式二:手动创建、管理事件循环
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = loop.create_task(say_hello())
loop.run_until_complete(task)
loop.close()
asyncio.run()
:封装了事件循环的创建、运行与关闭,推荐在主入口使用。- 手动管理:适合需要多次开启/关闭事件循环的场景。
五、协程与任务调度
async def fetch(idx):
print(f"Start {
idx}")
await asyncio.sleep(2)
print(f"Done {
idx}")
return idx * 2
async def main():