Python中的协程与异步编程中的异步上下文管理器与async with语句
字数 1254 2025-11-25 12:31:54
Python中的协程与异步编程中的异步上下文管理器与async with语句
1. 异步上下文管理器的背景与需求
在异步编程中,常见的资源操作(如文件I/O、数据库连接、网络请求)可能需要等待I/O完成。如果使用传统的同步上下文管理器(如with open(...)),在进入或退出上下文时可能会阻塞事件循环。异步上下文管理器通过__aenter__和__aexit__方法,允许在异步环境中非阻塞地管理资源。
2. 异步上下文管理器的协议定义
一个类要成为异步上下文管理器,需实现以下两个异步方法:
__aenter__(self): 在进入async with代码块时自动调用,可以执行异步初始化操作(如建立数据库连接)。__aexit__(self, exc_type, exc_val, exc_tb): 在退出代码块时自动调用,用于异步清理资源(如关闭连接),并能处理异常。
注意:
- 这两个方法必须定义为
async def。 __aexit__的参数用于捕获代码块内可能发生的异常(若未发生异常,这些参数为None)。
3. async with语句的工作流程
以下代码展示了async with的执行顺序:
async with AsyncManager() as resource:
# 代码块内的操作
pass
步骤分解:
- 调用
AsyncManager()创建实例。 - 自动调用实例的
__aenter__方法,并等待其完成(await隐式发生)。 __aenter__的返回值被赋值给resource。- 执行代码块内的逻辑。
- 无论代码块是否发生异常,都会自动调用
__aexit__方法,并等待其完成。 - 如果代码块内发生异常,异常信息会传递给
__aexit__,由其决定是否处理(返回True则抑制异常,否则异常传播)。
4. 手动实现异步上下文管理器示例
以下是一个模拟数据库连接的异步上下文管理器:
class AsyncDatabaseConnection:
async def __aenter__(self):
print("异步连接数据库...")
await asyncio.sleep(1) # 模拟异步连接
self.connection = "模拟的数据库连接"
return self.connection
async def __aexit__(self, exc_type, exc_val, exc_tb):
print("异步关闭数据库连接...")
await asyncio.sleep(0.5) # 模拟异步关闭
if exc_type:
print(f"发生异常: {exc_val}")
# 返回False时,异常会传播;返回True则抑制异常
# 使用示例
async def main():
async with AsyncDatabaseConnection() as conn:
print(f"使用连接: {conn}")
# 模拟数据库操作
await asyncio.sleep(2)
asyncio.run(main())
输出结果:
异步连接数据库...
使用连接: 模拟的数据库连接
异步关闭数据库连接...
5. 使用contextlib简化实现
Python的contextlib模块提供了@asynccontextmanager装饰器,可以用生成器快速创建异步上下文管理器:
from contextlib import asynccontextmanager
@asynccontextmanager
async def async_timer():
start = asyncio.get_event_loop().time()
print("开始计时")
try:
yield start # 在此处暂停,执行代码块
finally:
end = asyncio.get_event_loop().time()
print(f"耗时: {end - start:.2f}秒")
async def main():
async with async_timer() as start_time:
print(f"起始时间: {start_time}")
await asyncio.sleep(2)
asyncio.run(main())
输出:
开始计时
起始时间: 12345.67
耗时: 2.00秒
6. 常见应用场景
- 数据库连接池:如
async with pool.acquire() as conn。 - 异步文件I/O:如
aiofiles库的async with aiofiles.open(...) as f。 - 网络请求会话:如
aiohttp的async with aiohttp.ClientSession() as session。 - 事务管理:在异步ORM中管理数据库事务的提交与回滚。
7. 注意事项
- 若在
__aenter__或__aexit__中忘记使用await,可能导致资源未正确初始化或清理。 - 异步上下文管理器必须用在异步函数内(
async def),否则会报语法错误。 - 与同步上下文管理器(
__enter__/__exit__)协议不同,二者不能混用。