asyncio 用于 异步 I/O 和并发任务管理,它可以让 Python 程序在等待耗时操作(如网络请求、文件读写、数据库查询)时 不阻塞,从而同时处理更多任务,提高效率。
它在 Python 3.4 中引入,并在 Python 3.5 后结合 async/await 语法得到了广泛使用。
特点
- asyncio 通过 事件循环 + 协程 + 任务 来实现 高效并发 I/O。
- 不用线程,也能同时处理成百上千个耗时任务,非常适合网络爬虫、聊天机器人、异步 Web 服务等场景。
核心概念
事件循环(Event Loop)
asyncio 的核心是事件循环,它不断检查哪些任务准备好执行,然后调度它们。
类比:你可以把事件循环想象成一个“任务经理”,它不会阻塞自己,而是让任务按时完成。
关键特点:单线程运行,但能管理大量 I/O 并发。
协程(Coroutine)
使用 async def 定义的函数。
协程可以“暂停自己”,让事件循环去运行其他任务。
关键语法:await,表示等待耗时操作完成,同时允许其他协程运行。
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1) # 模拟异步操作
print("World")
asyncio.run(say_hello())
await 用于等待一个耗时操作完成,不会阻塞整个程序。
任务(Task)
协程本身只是一个可等待对象,封装成任务后就可以交给事件循环并发执行。
例如 asyncio.create_task(coro)。
Future 对象
表示一个异步操作的最终结果,可以在未来某个时间点得到结果。
asyncio.Task 是 Future 的子类。
await 关键字
await 是 Python 的关键字,用在 协程(async def 函数)里。
它的作用是 等待一个异步操作完成,同时不阻塞整个事件循环。
也就是说,当你 await 一个耗时操作时,Python 会“暂停当前协程”,去执行其他准备好的任务。
假设有一个耗时操作:
import time
def task():
time.sleep(2)
print(“完成”)
time.sleep(2) 是阻塞的,整个程序都会被卡住 2 秒,其他任务无法执行。
使用 asyncio 的异步方式:
import asyncio
async def task():
await asyncio.sleep(2) # 异步等待
print(“完成”)
asyncio.run(task())
asyncio.sleep(2) 是异步操作,await 会暂停这个协程,但事件循环可以去运行其他协程。
这样就不会阻塞程序,多个任务可以并发执行。
工作流程
- 你创建一个或多个协程(async def 函数)。
- 将协程封装成任务(可选)。
- 交给事件循环运行。
- 当协程遇到 await(耗时操作)时,会暂停自己,事件循环去运行其他任务。
- 耗时操作完成后,事件循环恢复协程继续执行。
- 所有任务完成后,事件循环退出。
✅ 特点:单线程 + 并发执行 + 非阻塞 I/O
示例:两个协程同时运行
import asyncio
async def task1():
print(“task1 开始”)
await asyncio.sleep(2)
print(“task1 结束”)
async def task2():
print(“task2 开始”)
await asyncio.sleep(1)
print(“task2 结束”)
async def main():
# 并发运行两个协程
await asyncio.gather(task1(), task2())
asyncio.run(main())
时间轴分析(秒为单位)
| 时间 | 事件 |
|---|---|
| 0s | main 启动,task1 和 task2 都开始执行 |
| 0s | task1 打印 "task1 开始" |
| 0s | task2 打印 "task2 开始" |
| 0s | 两个协程都遇到 await asyncio.sleep(),暂停自己,控制权交回事件循环 |
| 1s | task2 的 sleep 完成,事件循环恢复 task2 执行 |
| 1s | task2 打印 "task2 结束" |
| 2s | task1 的 sleep 完成,事件循环恢复 task1 执行 |
| 2s | task1 打印 "task1 结束" |
| 2s | 所有协程完成,main 结束,事件循环退出 |
关键点
- await 并不是阻塞,而是暂停当前协程。task1 和 task2 的 sleep 期间,CPU 可以跑其他任务。
- 事件循环调度协程恢复执行。1 秒时恢复 task2,2 秒时恢复 task1。
- 协程函数本身不会立即执行。只有 asyncio.run 或 await 等待它时,才开始执行。