python进阶-异步编程(大模型转型必会)
1. 异步编程简介
在IO密集型任务(如网络请求、数据库操作)中,传统的同步代码会阻塞等待,导致CPU空闲。asyncio提供基于事件循环的单线程并发方案:遇到IO等待时自动切换任务,从而大幅提升吞吐量。尤其适合AI服务中需要同时调用多个外部API的场景。
2. 核心概念与基本语法
2.1 定义协程
使用async def定义协程函数,调用它返回一个协程对象,不会立即执行。
import asyncio
async def say_hello():
"""一个简单的协程,打印Hello,等待1秒,再打印World"""
print("Hello")
await asyncio.sleep(1) # 模拟IO等待
print("World")
2.2 运行协程
Python 3.7+使用asyncio.run()启动事件循环并执行一个协程。
# 保存为 demo1.py 并运行
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
if __name__ == "__main__":
asyncio.run(say_hello())
2.3 await关键字
await挂起当前协程,等待另一个可等待对象(协程、Task、Future)完成。
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
async def main():
print("开始")
await say_hello() # 等待say_hello执行完毕
print("结束")
asyncio.run(main())
2.4 创建任务(Task)
用asyncio.create_task()将协程包装为任务,使其并发执行。
import asyncio
async def say_after(delay, msg):
await asyncio.sleep(delay)
print(msg)
async def main():
# 创建两个任务,它们将并发执行
task1 = asyncio.create_task(say_after(1, "Hello"))
task2 = asyncio.create_task(say_after(2, "World"))
print("任务已创建,等待完成...")
await task1 # 等待task1完成
await task2 # 等待task2完成
print("所有任务完成")
asyncio.run(main())
3. 并发执行多个协程
3.1 asyncio.gather() —— 收集结果
并发运行多个可等待对象,返回所有结果的列表(顺序与输入一致)。
import asyncio
async def fetch_data(delay, name):
await asyncio.sleep(delay)
return f"{name} 完成"
async def main():
results = await asyncio.gather(
fetch_data(1, "任务A"),
fetch_data(2, "任务B"),
fetch_data(1.5, "任务C")
)
print(results) # ['任务A 完成', '任务B 完成', '任务C 完成']
asyncio.run(main())
3.2 asyncio.wait() —— 细粒度控制
可以设置超时、等待条件(FIRST_COMPLETED等),返回完成和未完成的任务集合。
import asyncio
async def fetch_data(delay, name):
await asyncio.sleep(delay)
return f"{name} 完成"
async def main():
tasks = {asyncio.create_task(fetch_data(i, f"任务{i}")) for i in [3, 1, 2]}
# 等待第一个任务完成,超时2秒
done, pending = await asyncio.wait(tasks, timeout=2, return_when=asyncio.FIRST_COMPLETED)
print(f"已完成的任务数: {len(done)}")
for task in done:
print(task.result())
# 取消未完成的任务
for task in pending:
task.cancel()
print("已取消一个任务")
asyncio.run(main())
3.3 asyncio.as_completed() —— 按完成顺序处理
返回一个迭代器,按任务完成的先后顺序产出结果。
import asyncio
async def fetch_data(delay, name):
await asyncio.sleep(delay)
return f"{name} 完成"
async def main():
tasks = [fetch_data(i, f"任务{i}") for i in [3, 1, 2]]
for coro in asyncio.as_completed(tasks):
result = await coro
print(result) # 按实际完成顺序打印:任务1完成、任务2完成、任务3完成
asyncio.run(main())
4. 可等待对象(略)
可等待对象包括:协程、Task、Future。通常我们只需处理协程和Task。
5. 异步上下文管理器(async with)
用于管理需要异步初始化和清理的资源。自定义异步上下文管理器需实现__aenter__和__aexit__。
import asyncio
class AsyncResource:
async def __aenter__(self):
print("正在获取资源...")
await asyncio.sleep(1)
print("资源已获取")
return self
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("正在释放资源...")
await asyncio.sleep(1)
print("资源已释放")
async def main():
async with AsyncResource() as res:
print("使用资源中...")
asyncio.run(main())
实际应用:aiohttp的ClientSession、异步数据库连接等。
6. 异步迭代器(async for)
用于异步产生数据。可以使用异步生成器(yield在async def中)更简洁。
import asyncio
# 异步生成器
async def async_range(n):
for i in range(n):
await asyncio.sleep(0.1) # 模拟异步等待
yield i
async def main():
async for num in async_range(5):
print(num) # 0 1 2 3 4
asyncio.run(main())
7. 同步与异步的桥梁(run_in_executor)
在协程中执行阻塞的同步代码(如CPU密集型计算或传统同步库)时,应将其放入线程池,避免阻塞事件循环。
import asyncio
import time
def blocking_io():
"""模拟阻塞IO操作(如文件读写、同步请求)"""
time.sleep(2)
return "阻塞操作结果"
async def main():
loop = asyncio.get_running_loop()
# 在线程池中运行阻塞函数
result = await loop.run_in_executor(None, blocking_io)
print(result)
asyncio.run(main())
8. 异步队列(asyncio.Queue)
用于在多个协程之间安全传递数据,典型的生产者-消费者模型。
import asyncio
import random
async def producer(queue, n):
for i in range(n):
item = f"物品{i}"
await queue.put(item)
print(f"生产了 {item}")
await asyncio.sleep(random.random())
await queue.put(None) # 发送结束信号
async def consumer(queue):
while True:
item = await queue.get()
if item is None:
break
print(f"消费了 {item}")
queue.task_done()
async def main():
queue = asyncio.Queue(maxsize=5)
prod_task = asyncio.create_task(producer(queue, 10))
cons_task = asyncio.create_task(consumer(queue))
await asyncio.gather(prod_task, cons_task)
asyncio.run(main())
9. 超时与任务取消
9.1 超时(asyncio.wait_for)
设置协程的最大执行时间,超时抛出TimeoutError。
import asyncio
async def long_operation():
await asyncio.sleep(10)
return "完成"
async def main():
try:
result = await asyncio.wait_for(long_operation(), timeout=2.0)
except asyncio.TimeoutError:
print("操作超时")
asyncio.run(main())
9.2 取消任务(cancel())
调用task.cancel()请求取消任务,协程内可捕获CancelledError进行清理。
import asyncio
async def cancellable_task():
try:
await asyncio.sleep(10)
except asyncio.CancelledError:
print("任务被取消,进行清理...")
raise # 必须重新抛出以标记任务取消
async def main():
task = asyncio.create_task(cancellable_task())
await asyncio.sleep(1)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("任务已取消")
asyncio.run(main())
9.3 屏蔽取消(asyncio.shield)
保护某个协程不被取消,即使外层任务被取消。
import asyncio
async def critical_operation():
await asyncio.sleep(3)
print("关键操作完成")
async def main():
task = asyncio.create_task(critical_operation())
await asyncio.sleep(1)
# 即使主任务被取消,shield内部仍会继续执行
await asyncio.shield(task)
# 如果需要取消,可以单独取消
task.cancel()
asyncio.run(main())
10. 限制并发数量(信号量)
使用asyncio.Semaphore控制同时执行的任务数量,例如限制对API的并发请求。
import asyncio
import aiohttp # 需要安装:pip install aiohttp
async def fetch_url(sem, session, url):
async with sem:
print(f"正在请求: {url}")
async with session.get(url) as response:
return await response.text()
async def main():
urls = ["http://example.com"] * 10 # 模拟10个相同请求
sem = asyncio.Semaphore(3) # 最多同时3个请求
async with aiohttp.ClientSession() as session:
tasks = [fetch_url(sem, session, url) for url in urls]
results = await asyncio.gather(*tasks)
print(f"获取到 {len(results)} 个响应")
# 注意:example.com可能无法真正连接,仅演示结构
# 实际使用时请替换为真实URL
asyncio.run(main())
11. 在AI服务中的应用场景
11.1 并发调用多个AI模型API
假设需要同时对文本进行三种不同的AI处理。
import asyncio
import aiohttp
async def call_ai_api(session, text, model_name):
"""模拟调用AI API"""
url = f"https://api.example.com/{model_name}/predict"
payload = {"text": text}
async with session.post(url, json=payload) as resp:
return await resp.json()
async def handle_request(text):
async with aiohttp.ClientSession() as session:
tasks = [
call_ai_api(session, text, "sentiment"),
call_ai_api(session, text, "ner"),
call_ai_api(session, text, "summary")
]
results = await asyncio.gather(*tasks)
return {"text": text, "sentiment": results[0], "ner": results[1], "summary": results[2]}
async def main():
result = await handle_request("I love Python async programming!")
print(result)
# 实际运行时需要真实API端点,此处仅展示结构
asyncio.run(main())
11.2 异步数据库访问(使用asyncpg)
需要安装asyncpg:pip install asyncpg
import asyncio
import asyncpg
async def fetch_users():
conn = await asyncpg.connect(
user='your_user', password='your_pass',
database='your_db', host='localhost'
)
rows = await conn.fetch('SELECT id, name FROM users LIMIT 10')
await conn.close()
return rows
async def main():
users = await fetch_users()
for user in users:
print(dict(user))
# 注意:需替换为真实数据库连接信息
# asyncio.run(main())
11.3 异步微服务网关
同时向后端多个服务发起请求,合并响应。
import asyncio
import aiohttp
async def fetch_service(session, service_url, payload):
async with session.post(service_url, json=payload) as resp:
return await resp.json()
async def gateway(request_data):
services = [
"http://ai-service1/predict",
"http://ai-service2/predict",
"http://ai-service3/predict"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch_service(session, url, request_data) for url in services]
results = await asyncio.gather(*tasks)
return results
async def main():
data = {"input": "test data"}
combined = await gateway(data)
print(combined)
asyncio.run(main())
11.4 流式数据处理(异步迭代器 + 队列)
从消息队列消费消息,并发处理。
import asyncio
import random
async def message_generator(queue):
"""模拟消息生产者"""
for i in range(20):
await asyncio.sleep(random.random() * 0.5)
msg = f"消息{i}"
await queue.put(msg)
print(f"放入: {msg}")
await queue.put(None) # 结束信号
async def process_message(msg):
"""模拟AI处理"""
await asyncio.sleep(random.random())
return f"处理结果: {msg}"
async def worker(worker_id, queue, results):
while True:
msg = await queue.get()
if msg is None:
await queue.put(None) # 传递结束信号给其他worker
break
result = await process_message(msg)
print(f"Worker {worker_id} 处理了 {msg} -> {result}")
results.append(result)
async def main():
queue = asyncio.Queue()
results = []
# 启动生产者和消费者
producer = asyncio.create_task(message_generator(queue))
workers = [asyncio.create_task(worker(i, queue, results)) for i in range(3)]
await producer
await asyncio.gather(*workers)
print(f"总共处理了 {len(results)} 条消息")
asyncio.run(main())
12. 常见陷阱与调试技巧
12.1 忘记await
import asyncio
async def bad_example():
# 错误:直接调用协程但没有await,协程不会执行
asyncio.sleep(1) # 缺少await,返回一个协程对象,未执行
print("这行会立即打印")
async def good_example():
await asyncio.sleep(1)
print("1秒后打印")
asyncio.run(good_example())
12.2 在协程中使用阻塞IO
import asyncio
import time
async def blocking_mistake():
time.sleep(2) # 阻塞整个事件循环!
print("完成")
async def main():
# 错误示例:会阻塞2秒,期间无法处理其他任务
await blocking_mistake()
# 正确做法:使用run_in_executor
12.3 异常处理
import asyncio
async def might_fail(should_fail):
if should_fail:
raise ValueError("出错了")
await asyncio.sleep(1)
return "成功"
async def main():
tasks = [might_fail(False), might_fail(True)]
# 使用return_exceptions=True让gather返回异常对象而非抛出
results = await asyncio.gather(*tasks, return_exceptions=True)
for r in results:
if isinstance(r, Exception):
print(f"捕获到异常: {r}")
else:
print(f"结果: {r}")
asyncio.run(main())
12.4 调试模式
设置环境变量PYTHONASYNCIODEBUG=1或在代码中启用:
asyncio.run(main(), debug=True)
12.5 任务清理
import asyncio
async def worker():
try:
while True:
await asyncio.sleep(1)
except asyncio.CancelledError:
print("worker被取消,清理中...")
raise
async def main():
task = asyncio.create_task(worker())
await asyncio.sleep(3)
task.cancel()
try:
await task
except asyncio.CancelledError:
print("worker已清理完毕")
asyncio.run(main())
13. 进阶:与Web框架结合(FastAPI示例)
需要安装fastapi和uvicorn:pip install fastapi uvicorn
# server.py
from fastapi import FastAPI
import asyncio
import httpx # 异步HTTP客户端,需安装:pip install httpx
app = FastAPI()
async def call_ai(prompt: str):
async with httpx.AsyncClient() as client:
# 这里替换为真实的AI服务地址
resp = await client.post("https://api.openai.com/v1/completions",
json={"prompt": prompt, "max_tokens": 50},
headers={"Authorization": "Bearer YOUR_KEY"})
return resp.json()
@app.post("/process")
async def process(prompt: str):
# 并发调用多个模型(这里模拟调用同一个模型两次)
results = await asyncio.gather(
call_ai(prompt),
call_ai(prompt + " (enhanced)")
)
return {"prompt": prompt, "results": results}
# 运行:uvicorn server:app --reload
启动服务后,用curl测试:curl -X POST "http://127.0.0.1:8000/process?prompt=Hello"
14. 总结
asyncio让Python能够高效处理IO密集型并发任务。- 核心模式:定义协程(
async def)、创建任务(create_task)、并发执行(gather/wait)。 - 掌握异步上下文管理器、异步迭代器、队列、信号量等工具,构建复杂系统。
- 注意将阻塞操作放入线程池,合理处理超时和取消。
- 在AI服务中,结合
aiohttp、异步数据库驱动和异步Web框架,可实现高吞吐量微服务。

浙公网安备 33010602011771号