异步 I/O (Async/Await)
在现代 Web 开发和网络编程中,异步 I/O (Asynchronous I/O) 是一个非常重要的概念。Python 3.4 引入了 asyncio 库,并在 3.5 中引入了 async 和 await 关键字,使得编写并发代码变得像写同步代码一样直观。
1. 同步 vs 异步
- 同步 (Synchronous):代码按顺序执行。如果一行代码在等待(比如等待网络请求或者磁盘读取),整个程序都会停下来,直到在该行完成。
- 异步 (Asynchronous):程序不需要等待耗时操作完成就可以继续执行其他任务。当那个耗时操作完成后,程序会回头处理它的结果。
想象你在做饭: * 同步:烧水 -> 等水开 -> 下面条。 * 异步:烧水 -> (在等水开的时候) 切葱花、调酱料 -> 水开了 -> 下面条。
2. 核心概念:协程 (Coroutine)
使用 async def 定义的函数称为协程函数。调用它不会立即执行,而是返回一个协程对象。
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1) # 模拟耗时操作,非阻塞
print("World")
# 直接调用不会运行
# say_hello()
# result: <coroutine object say_hello at 0x...>
# 需要通过 asyncio.run() 来运行
if __name__ == "__main__":
asyncio.run(say_hello())
注意:必须使用 await asyncio.sleep(1) 而不是 time.sleep(1)。time.sleep 是同步阻塞的,会卡住整个事件循环。
3. 并发执行:asyncio.gather
异步的真正威力在于同时做多件事。
import asyncio
import time
async def download_file(name, duration):
print(f"开始下载 {name}...")
await asyncio.sleep(duration) # 模拟下载耗时
print(f"{name} 下载完成!")
return name
async def main():
print(f"启动任务于 {time.strftime('%X')}")
# 创建三个并发任务
# gather 会等待所有任务完成
results = await asyncio.gather(
download_file("Photo.jpg", 2),
download_file("Video.mp4", 3),
download_file("Audio.mp3", 1)
)
print(f"所有任务完成于 {time.strftime('%X')}")
print(f"下载列表: {results}")
if __name__ == "__main__":
asyncio.run(main())
输出预期: 虽然 Video.mp4 需要 3 秒,但整个程序只会运行约 3 秒(而不是 2+3+1=6 秒),因为它们是同时进行的。
4. await 到底在等什么?
await 后面通常接:
1. 另一个协程 (await other_coroutine())
2. 一个 Future 对象(底层对象,通常库作者才用)
3. 实现了 __await__ 方法的对象
当代码执行到 await 时,它告诉事件循环:“我要在这里等一下结果,你去忙别的吧。”
5. 常见误区
不要在异步函数中使用阻塞代码
如果在 async def 中使用了 time.sleep() 或者进行了非常繁重的 CPU 计算(如大型矩阵乘法),如果不做特殊处理,它们依然会阻塞整个线程,导致其他异步任务无法运行。
异步主要优化的是 I/O 密集型 任务(网络请求、数据库读写)。对于 CPU 密集型 任务,应该使用多进程 (multiprocessing)。
6. 实战场景
在爬虫、Web 后端(如 FastAPI)、Discord/Telegram 机器人等场景中,异步编程是标配。
# 伪代码:异步爬虫示例
import aiohttp # 需要安装 aiohttp 库
import asyncio
async def fetch_url(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.text()
async def main():
html = await fetch_url("https://www.python.org")
print(html[:100])
总结
async def定义协程。await挂起当前协程,等待结果,让出控制权。asyncio.run()是程序的入口。asyncio.gather()用于并发运行多个协程。
下一步
恭喜你!你已经掌握了 Python 中最核心的高级特性。现在你具备了编写高效、优雅、工程化 Python 代码的能力。
是时候将这些工具应用到更复杂的领域了——让我们开始探索 算法与数据结构,去解决那些烧脑的难题吧!