问题(我认为)
contextlib.asynccontextmanager
文档给出了这个例子:
@asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
在我看来,这可能会泄漏资源。如果此代码的任务是 cancelled 而此代码在其 await release_db_connection(conn)
行上,则发布可能会中断。 asyncio.CancelledError
将从 finally
块内的某处向上传播,阻止后续清理代码运行。因此,实际上,如果您正在实现一个处理超时请求的 Web 服务器,则在完全错误的时间触发超时可能会导致数据库连接泄漏。
完整的可运行示例
import asyncio
from contextlib import asynccontextmanager
async def acquire_db_connection():
await asyncio.sleep(1)
print("Acquired database connection.")
return "<fake connection object>"
async def release_db_connection(conn):
await asyncio.sleep(1)
print("Released database connection.")
@asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
async def do_stuff_with_connection():
async with get_connection() as conn:
await asyncio.sleep(1)
print("Did stuff with connection.")
async def main():
task = asyncio.create_task(do_stuff_with_connection())
# Cancel the task just as the context manager running
# inside of it is executing its cleanup code.
await asyncio.sleep(2.5)
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
print("Done.")
asyncio.run(main())
Python 3.7.9 上的输出:Acquired database connection.
Did stuff with connection.
Done.
请注意,永远不会打印 Released database connection
。我的问题
.cancel()
表示“优雅地取消,清理沿途使用的任何资源”。 (否则,他们为什么要将取消实现为异常传播?)但我可能是错的。例如,也许 .cancel()
意味着快速而不是优雅。是否有权威来源澄清 .cancel()
在这里应该做什么? 最佳答案
您可以使用 asyncio.shield
保护任务以保证上下文管理器正常关闭,我只在 main()
中进行了更改:
async def main():
task = asyncio.create_task(do_stuff_with_connection())
# shield context manager from cancellation
sh_task = asyncio.shield(task)
# Cancel the task just as the context manager running
# inside of it is executing its cleanup code.
await asyncio.sleep(2.5)
sh_task.cancel() # cancel shielded task
try:
await sh_task
except asyncio.CancelledError:
pass
await asyncio.sleep(5) # wait till shielded task is done
print("Done.")
关于python - 异步上下文管理器是否需要保护它们的清理代码不被取消?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68624314/