
在现代网络应用开发中,高并发处理能力是衡量系统性能的重要指标。Python的asyncio库自3.4版本引入以来,已经成为构建高性能异步应用的标准方案。无论是Web服务器、爬虫系统还是微服务架构,asyncio都能以极低的资源开销实现高效的I/O并发。本文将从事件循环原理出发,结合实际代码示例,帮助你全面掌握Python asyncio异步编程。
一、asyncio的核心概念:事件循环与协程
asyncio的核心是事件循环(Event Loop),它负责调度和执行所有的异步任务。协程(Coroutine)是使用 async def 定义的函数,调用后不会立即执行,而是返回一个协程对象,需要通过事件循环来驱动运行。
import asyncio
async def say_hello(name: str, delay: int):
await asyncio.sleep(delay)
print(f"Hello, {name}! (after {delay}s)")
return f"greeted {name}"
async def main():
result = await say_hello("World", 2)
print(f"Result: {result}")
asyncio.run(main())
await 关键字是协程的关键,它将控制权交还给事件循环,让其他协程有机会执行。当await的操作完成后,事件循环会恢复该协程的执行。
二、并发执行多个任务:gather与TaskGroup
实际开发中,我们通常需要同时执行多个异步操作。asyncio.gather() 是最常用的并发执行方式,它会同时启动所有协程并等待全部完成。

import asyncio
import time
async def fetch_data(url: str, delay: int) -> dict:
print(f"开始请求: {url}")
await asyncio.sleep(delay)
return {"url": url, "status": 200, "delay": delay}
async def main():
start = time.time()
urls = [
("https://api.example.com/users", 2),
("https://api.example.com/posts", 1),
("https://api.example.com/comments", 3),
]
tasks = [fetch_data(url, delay) for url, delay in urls]
results = await asyncio.gather(*tasks)
elapsed = time.time() - start
print(f"总耗时: {elapsed:.1f}s(而非串行的6s)")
for r in results:
print(f" {r['url']} -> {r['status']}")
asyncio.run(main())
Python 3.11引入了 ,它提供了更安全的并发管理,能自动处理异常传播:
async def main_taskgroup():
async with asyncio.TaskGroup() as tg:
task1 = tg.create_task(fetch_data("https://api.a.com", 2))
task2 = tg.create_task(fetch_data("https://api.b.com", 1))
print(task1.result(), task2.result())
三、异步上下文管理器与异步迭代器
asyncio不仅支持异步函数,还支持异步上下文管理器(async with)和异步迭代器(async for),这在管理数据库连接池、消息队列等资源时非常实用。
import asyncio
class AsyncConnectionPool:
def __init__(self, max_size: int = 5):
self.max_size = max_size
self._pool = asyncio.Queue(maxsize=max_size)
async def connect(self):
for i in range(self.max_size):
await self._pool.put(f"conn_{i}")
async def __aenter__(self):
self.conn = await self._pool.get()
print(f"获取连接: {self.conn}")
return self.conn
async def __aexit__(self, exc_type, exc_val, exc_tb):
await self._pool.put(self.conn)
print(f"归还连接: {self.conn}")
async def main():
pool = AsyncConnectionPool(max_size=3)
await pool.connect()
async with pool as conn:
await asyncio.sleep(0.5)
print(f"使用 {conn} 执行查询")
asyncio.run(main())
四、异步生成器与流式数据处理
异步生成器使用 async for 逐个产出数据,非常适合处理流式数据、分页API响应等场景:
import asyncio
async def async_range(n: int):
for i in range(n):
await asyncio.sleep(0.1)
yield i
async def paginate_api(page_size: int = 10, total_pages: int = 5):
for page in range(1, total_pages + 1):
await asyncio.sleep(0.3)
data = [f"item_{page}_{i}" for i in range(page_size)]
yield {"page": page, "data": data}
async def main():
async for num in async_range(5):
print(f"收到数据: {num}")
async for page_result in paginate_api():
print(f"第{page_result['page']}页: {len(page_result['data'])}条记录")
asyncio.run(main())
五、实战技巧与常见陷阱
在使用asyncio时,有几个重要的实践建议:
1. 避免在协程中使用阻塞操作:如 time.sleep()、requests.get() 等会阻塞整个事件循环。应使用 aiohttp、aiofiles 等异步替代库。
2. 使用信号量控制并发数:防止同时打开过多连接导致资源耗尽。
import asyncio
import aiohttp
async def fetch_with_limit(sem, url, session):
async with sem:
print(f"请求: {url}")
async with session.get(url) as resp:
return await resp.text()
async def main():
sem = asyncio.Semaphore(5)
urls = [f"https://httpbin.org/delay/{i % 3}" for i in range(20)]
async with aiohttp.ClientSession() as session:
tasks = [fetch_with_limit(sem, url, session) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
print(f"完成 {len(results)} 个请求")
3. 超时控制:使用 asyncio.wait_for() 或 asyncio.timeout() 避免协程无限挂起。
async def slow_operation():
await asyncio.sleep(100)
return "done"
async def main_timeout():
try:
result = await asyncio.wait_for(slow_operation(), timeout=3.0)
except asyncio.TimeoutError:
print("操作超时,已取消")
asyncio.run(main_timeout())
4. 正确处理异常:使用 return_exceptions=True 或 TaskGroup 来妥善处理并发任务中的异常,避免单个任务失败导致所有任务被取消。

总结
Python asyncio通过事件循环和协程机制,以同步代码的编写风格实现了高效的异步I/O操作。核心要点包括:使用 async def 定义协程、await 等待异步操作完成、gather() 或 TaskGroup 并发执行任务、信号量控制并发数、以及使用异步上下文管理器管理资源。掌握这些技巧,你就能用Python构建出高性能的异步应用系统。
汤不热吧