Python 异步编程实战:asyncio 核心概念与最佳实践
前言
在高性能网络应用和 IO 密集型任务中,异步编程已成为 Python 开发者的必备技能。本文将深入讲解 asyncio 的核心概念,并通过实战代码帮助你掌握异步编程的精髓。
一、什么是异步编程?
异步编程允许程序在等待某些操作(如网络请求、文件读写)完成时,转而执行其他任务,而不是阻塞等待。这大大提高了程序的并发性能。
同步 vs 异步
```python
同步代码 - 阻塞式
import time
def fetch_data_sync():
print("开始获取数据...")
time.sleep(2) # 阻塞 2 秒
print("数据获取完成")
return "data"
fetch_data_sync()
fetch_data_sync() # 必须等上一个完成才能执行
总耗时:4 秒
```
```python
异步代码 - 非阻塞式
import asyncio
async def fetch_data_async():
print("开始获取数据...")
await asyncio.sleep(2) # 非阻塞等待
print("数据获取完成")
return "data"
async def main():
两个任务并发执行
results = await asyncio.gather(
fetch_data_async(),
fetch_data_async()
)
return results
asyncio.run(main())
总耗时:约 2 秒
```
二、asyncio 核心概念
1. async/await 关键字
-
`async def`:定义协程函数
-
`await`:等待协程执行完成
```python
async def say_hello():
await asyncio.sleep(1)
print("Hello")
async def say_world():
await asyncio.sleep(1)
print("World")
async def main():
await say_hello()
await say_world()
asyncio.run(main())
```
2. Task - 并发执行
```python
async def worker(name, delay):
for i in range(3):
await asyncio.sleep(delay)
print(f"{name} 执行第 {i+1} 次")
async def main():
创建两个并发任务
task1 = asyncio.create_task(worker("任务 A", 1))
task2 = asyncio.create_task(worker("任务 B", 1.5))
等待所有任务完成
await asyncio.gather(task1, task2)
asyncio.run(main())
```
3. Semaphore - 控制并发数
```python
async def fetch_with_limit(semaphore, url_id):
async with semaphore:
print(f"开始获取 URL {url_id}")
await asyncio.sleep(1)
print(f"完成获取 URL {url_id}")
async def main():
限制最多同时 3 个并发
semaphore = asyncio.Semaphore(3)
tasks = [
fetch_with_limit(semaphore, i)
for i in range(10)
]
await asyncio.gather(*tasks)
asyncio.run(main())
```
三、实战:异步 HTTP 请求
使用 aiohttp 进行异步 HTTP 请求:
```python
import aiohttp
import asyncio
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://jsonplaceholder.typicode.com/posts/1"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks)
for i, result in enumerate(results):
print(f"URL {i+1} 响应长度:{len(result)}")
asyncio.run(main())
```
四、最佳实践
1. 避免阻塞操作
❌ 错误示例:
```python
async def bad_example():
time.sleep(1) # 阻塞整个事件循环!
```
✅ 正确示例:
```python
async def good_example():
await asyncio.sleep(1) # 非阻塞
```
2. 使用 asyncio.gather 处理多个任务
```python
async def process_items(items):
async def process_one(item):
await asyncio.sleep(0.5)
return item * 2
results = await asyncio.gather(*[
process_one(item) for item in items
])
return results
```
3. 超时控制
```python
async def fetch_with_timeout():
try:
result = await asyncio.wait_for(
some_async_operation(),
timeout=5.0
)
return result
except asyncio.TimeoutError:
print("操作超时")
return None
```
五、常见陷阱
1. 忘记 await
```python
错误:协程不会被执行
async def wrong():
asyncio.sleep(1) # 只是创建了协程对象
正确
async def right():
await asyncio.sleep(1)
```
2. 在同步函数中调用异步函数
```python
错误
def sync_function():
asyncio.run(async_func()) # 可能导致事件循环错误
正确:整个调用链都应该是异步的
async def async_function():
await async_func()
```
总结
异步编程是 Python 高性能应用的关键技术。掌握 asyncio 需要理解:
-
**事件循环**:异步编程的核心引擎
-
**协程**:使用 async/await 定义的轻量级线程
-
**Task**:并发执行的协程包装器
-
**非阻塞 IO**:避免使用同步阻塞操作
通过本文的示例代码,相信你已经对 asyncio 有了更深入的理解。在实际项目中,合理使用异步编程可以显著提升应用性能!
**参考资料:**
-
Python 官方 asyncio 文档
-
aiohttp 官方文档
-
《Python 异步编程实战》