跳转至

异步 I/O (Async/Await)

在现代 Web 开发和网络编程中,异步 I/O (Asynchronous I/O) 是一个非常重要的概念。Python 3.4 引入了 asyncio 库,并在 3.5 中引入了 asyncawait 关键字,使得编写并发代码变得像写同步代码一样直观。

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 代码的能力。

是时候将这些工具应用到更复杂的领域了——让我们开始探索 算法与数据结构,去解决那些烧脑的难题吧!

👉 前往下一部分:算法概述