Python异步编程深度解析:从asyncio到实战应用
引言
异步编程是现代Python后端开发中不可或缺的技能。作为从Python转向Rust的后端开发者,我发现Python的异步生态非常成熟,尤其是asyncio库提供了强大的异步编程能力。本文将深入探讨Python异步编程的核心概念和最佳实践,帮助你掌握从协程到异步IO的完整知识体系。
一、异步编程基础
1.1 同步vs异步
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 同步 | 阻塞等待 | CPU密集型任务 |
| 异步 | 非阻塞等待 | IO密集型任务 |
1.2 协程概念
协程是一种轻量级的并发编程方式,允许在单个线程中实现并发:
import asyncio
async def hello():
print("Hello")
await asyncio.sleep(1)
print("World")
asyncio.run(hello())
1.3 事件循环
事件循环是异步编程的核心,负责调度协程的执行:
async def main():
loop = asyncio.get_running_loop()
print(f"Loop: {loop}")
print(f"Loop is running: {loop.is_running()}")
asyncio.run(main())
二、asyncio核心API
2.1 创建协程
async def fetch_data(url):
print(f"Fetching {url}")
await asyncio.sleep(1) # 模拟IO操作
return f"Data from {url}"
async def main():
# 创建协程对象
coro = fetch_data("https://example.com")
print(f"Coroutine: {coro}")
# 执行协程
result = await coro
print(f"Result: {result}")
asyncio.run(main())
2.2 并发执行协程
async def fetch_data(url, delay):
print(f"Start fetching {url}")
await asyncio.sleep(delay)
return f"Data from {url}"
async def main():
# 使用asyncio.gather并发执行
results = await asyncio.gather(
fetch_data("https://api1.com", 2),
fetch_data("https://api2.com", 1),
fetch_data("https://api3.com", 3)
)
print(f"Results: {results}")
asyncio.run(main())
2.3 任务管理
async def task_function(name, delay):
print(f"Task {name} started")
await asyncio.sleep(delay)
print(f"Task {name} completed")
return f"Result from {name}"
async def main():
# 创建任务
task1 = asyncio.create_task(task_function("A", 2))
task2 = asyncio.create_task(task_function("B", 1))
# 等待任务完成
result1 = await task1
result2 = await task2
print(f"Results: {result1}, {result2}")
asyncio.run(main())
三、异步IO操作
3.1 文件操作
async def read_file_async(file_path):
loop = asyncio.get_running_loop()
# 使用run_in_executor执行同步IO
with open(file_path, 'r') as f:
contents = await loop.run_in_executor(None, f.read)
return contents
async def main():
content = await read_file_async('example.txt')
print(f"File content: {content[:100]}")
asyncio.run(main())
3.2 网络请求
import aiohttp
async def fetch_url(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
async with aiohttp.ClientSession() as session:
html = await fetch_url(session, 'https://example.com')
print(f"HTML length: {len(html)}")
asyncio.run(main())
3.3 数据库操作
import asyncpg
async def query_database():
conn = await asyncpg.connect('postgresql://user:pass@localhost/db')
result = await conn.fetch('SELECT * FROM users LIMIT 10')
await conn.close()
return result
asyncio.run(query_database())
四、高级异步模式
4.1 并发控制
async def worker(name, queue):
while True:
item = await queue.get()
print(f"Worker {name} processing {item}")
await asyncio.sleep(1)
queue.task_done()
async def main():
queue = asyncio.Queue()
# 添加任务到队列
for i in range(10):
queue.put_nowait(i)
# 创建多个worker
workers = []
for i in range(3):
task = asyncio.create_task(worker(f"W{i}", queue))
workers.append(task)
# 等待队列清空
await queue.join()
# 取消worker
for task in workers:
task.cancel()
await asyncio.gather(*workers, return_exceptions=True)
asyncio.run(main())
4.2 超时处理
async def slow_operation():
await asyncio.sleep(5)
return "Done"
async def main():
try:
result = await asyncio.wait_for(slow_operation(), timeout=2)
print(f"Result: {result}")
except asyncio.TimeoutError:
print("Operation timed out")
asyncio.run(main())
4.3 信号处理
async def handle_signal():
loop = asyncio.get_running_loop()
def shutdown():
print("Shutting down gracefully...")
loop.stop()
loop.add_signal_handler(signal.SIGINT, shutdown)
# 保持运行
await asyncio.Event().wait()
asyncio.run(handle_signal())
五、实战:异步Web服务器
5.1 使用FastAPI
from fastapi import FastAPI
import asyncio
app = FastAPI()
@app.get("/")
async def root():
await asyncio.sleep(1)
return {"message": "Hello World"}
@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
await asyncio.sleep(0.5)
return {"item_id": item_id, "q": q}
5.2 异步任务队列
from fastapi import FastAPI, BackgroundTasks
import asyncio
app = FastAPI()
async def process_data(data: str):
# 模拟耗时操作
await asyncio.sleep(5)
print(f"Processed data: {data}")
@app.post("/process")
async def trigger_processing(data: str, background_tasks: BackgroundTasks):
background_tasks.add_task(process_data, data)
return {"message": "Processing started"}
六、性能优化
6.1 避免阻塞调用
# 错误示例:在异步函数中调用同步阻塞函数
async def bad_example():
# 这会阻塞事件循环
time.sleep(1) # ❌
# 正确示例:使用异步替代或线程池
async def good_example():
await asyncio.sleep(1) # ✅
# 或者使用线程池执行阻塞操作
async def better_example():
loop = asyncio.get_running_loop()
await loop.run_in_executor(None, blocking_function)
6.2 使用连接池
async def create_pool():
return await asyncpg.create_pool(
user='user',
password='pass',
database='db',
host='localhost',
min_size=5,
max_size=20
)
6.3 批量操作
async def batch_insert(pool, items):
async with pool.acquire() as conn:
async with conn.transaction():
for item in items:
await conn.execute(
'INSERT INTO table (col1, col2) VALUES ($1, $2)',
item['col1'], item['col2']
)
七、常见陷阱
7.1 忘记await
async def get_data():
return "data"
async def main():
# 错误:没有await
result = get_data() # 返回协程对象,不是结果
# 正确
result = await get_data()
7.2 混合同步和异步
async def async_func():
# 错误:调用同步阻塞函数
requests.get('https://example.com') # 阻塞事件循环
# 正确:使用异步HTTP客户端
async with aiohttp.ClientSession() as session:
async with session.get('https://example.com') as resp:
await resp.text()
7.3 线程安全问题
# 注意:共享状态需要适当的同步
shared_data = []
async def add_item(item):
# 多个协程同时操作可能导致问题
shared_data.append(item) # 需要考虑线程安全
八、总结
Python的异步编程为IO密集型应用提供了高效的解决方案。通过掌握asyncio的核心概念和最佳实践,我们可以构建高性能的异步应用。
关键要点:
- 使用协程:通过async/await定义异步函数
- 并发执行:使用asyncio.gather并发多个协程
- 避免阻塞:不要在协程中调用同步阻塞函数
- 任务管理:使用Task管理异步任务
- 资源管理:正确使用连接池和上下文管理器
从Python转向Rust后,我发现Rust的Tokio异步运行时在性能和类型安全方面有很大优势,但Python的异步生态更加成熟和易用。
延伸阅读
- asyncio官方文档
- FastAPI官方文档
- aiohttp官方文档
- 《Python异步编程实战》

1206

被折叠的 条评论
为什么被折叠?



