Python 异步编程:asyncio 原理与实战指南
异步编程是处理 IO 密集型任务的利器。Python 的 asyncio 库提供了协程、事件循环等核心机制,让单线程 Python 也能高效处理大量并发请求。
一、同步 vs 异步 vs 多线程
- 同步:顺序执行,简单直观,IO 等待时 CPU 空闲
- 多线程:并发 IO,但有 GIL 限制,线程切换有开销
- 异步协程:单线程内切换,无 GIL 限制,IO 等待时切换任务,适合高并发网络服务
二、asyncio 核心概念
import asyncio
# 协程函数
async def fetch_data(url):
print(f"开始请求 {url}")
await asyncio.sleep(1) # 模拟 IO 等待,让出控制权
return f"数据来自 {url}"
# 并发执行多个协程
async def main():
tasks = [
fetch_data("https://api1.example.com"),
fetch_data("https://api2.example.com"),
fetch_data("https://api3.example.com"),
]
# gather 并发执行,总耗时约1秒(而非3秒)
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
三、事件循环工作原理
事件循环是 asyncio 的核心,它持续监听 IO 事件和回调:
- 执行协程直到遇到
await(IO 等待) - 将当前协程挂起,切换到其他就绪协程
- IO 完成后,恢复挂起的协程继续执行
四、实战:异步 HTTP 请求(aiohttp)
import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.json()
async def fetch_all(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
return await asyncio.gather(*tasks)
# 并发请求100个接口,耗时约等于最慢的那个
urls = [f"https://api.example.com/item/{i}" for i in range(100)]
results = asyncio.run(fetch_all(urls))
总结
asyncio 适合 IO 密集型任务(网络请求、数据库查询、文件读写),不适合 CPU 密集型任务(计算、图像处理)。理解事件循环原理,才能写出正确、高效的异步代码。
