传统的 Python 编程通常采用同步阻塞模式。在面对大量 I/O 密集型任务(如网络爬虫、API 请求、文件读取)时,同步代码会让 CPU 处于空闲等待状态,极大限制了程序效率。

asyncio 是 Python 原生支持的异步编程库,旨在通过单线程和事件循环(Event Loop)并发处理数以千计的 I/O 操作。本文将带你用最短的时间理解并上手 Python 异步开发。

核心概念三要素

  1. async/await 关键字
    • async def 定义的函数称为协程函数(Coroutine Function),调用它会返回一个协程对象,而不会直接执行其内部代码。
    • 使用 await 挂起耗时 I/O 任务,让出 CPU 执行权,使其他协程能够继续运行。
  2. 事件循环(Event Loop)
    • 异步应用的核心调度器,负责监听和循环分发协程任务,并在挂起的 I/O 准备就绪时恢复执行协程。
  3. 可等待对象(Awaitables)
    • 可以在 await 表达式中使用的对象,通常包括协程、任务(Tasks)和 Future 对象。

实战:同步 VS 异步并发

我们模拟一个需要调用多个网络接口的场景,每个网络调用大约耗时 1.5 秒。

传统的同步写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time

def fetch_data(api_id):
print(f"开始获取接口数据: {api_id}")
time.sleep(1.5) # 模拟阻塞的同步网络延迟
print(f"完成获取接口数据: {api_id}")
return f"数据 {api_id}"

def main():
start = time.time()
results = [fetch_data(i) for i in range(3)]
print(f"同步耗时: {time.time() - start:.2f} 秒")

if __name__ == "__main__":
main()
  • 同步执行耗时:约 4.5 秒(任务依次排队执行)。

高效的 asyncio 异步并发写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import asyncio
import time

async def fetch_data_async(api_id):
print(f"开始获取接口数据: {api_id}")
await asyncio.sleep(1.5) # 模拟非阻塞的异步网络延迟
print(f"完成获取接口数据: {api_id}")
return f"数据 {api_id}"

async def main():
start = time.time()
# 使用 gather 将多个协程并发打包执行
results = await asyncio.gather(
fetch_data_async(1),
fetch_data_async(2),
fetch_data_async(3)
)
print(f"异步耗时: {time.time() - start:.2f} 秒")

if __name__ == "__main__":
asyncio.run(main()) # 启动事件循环并执行入口协程
  • 异步并发耗时:约 1.5 秒!所有任务几乎在同一时间启动,并在等待期间交出了 CPU 控制权。

总结要点

  • 在异步协程函数中,绝不能使用任何传统的同步阻塞库(如原生的 time.sleep() 或同步的 requests 请求库)。必须使用其对应的异步替代版本(如 asyncio.sleep()aiohttp/httpx 库),否则整个事件循环会被彻底阻塞,失去异步的意义。
  • 使用 asyncio.run() 启动整个事件循环,并且仅在主入口函数调用一次即可。