面试题答案
一键面试实现思路
- 引入上下文对象:使用
contextlib.Context
相关工具,如asyncio.CancelledError
和contextvars.ContextVar
。asyncio.CancelledError
是协程被取消时抛出的异常,我们可以在协程内部捕获这个异常来进行清理工作。ContextVar
可以在不同的协程之间传递上下文信息,包括取消信号。 - 传递上下文:在创建和调用嵌套协程时,将上下文对象作为参数传递,这样所有协程都能感知到取消信号。
- 捕获取消信号:在每个协程内部,捕获
asyncio.CancelledError
异常,在异常处理中执行清理工作,如关闭文件、释放资源等。
关键代码片段
import asyncio
import contextvars
# 创建一个ContextVar用于传递取消信号
cancel_ctx = contextvars.ContextVar('cancel_ctx', default=False)
async def nested_task():
try:
while not cancel_ctx.get():
# 执行任务逻辑
await asyncio.sleep(1)
print('Nested task is running')
except asyncio.CancelledError:
# 捕获取消信号,进行清理工作
print('Nested task is cancelled, doing cleanup')
async def main_task():
nested = asyncio.create_task(nested_task())
try:
await asyncio.sleep(5)
# 模拟5秒后外部发起取消请求
cancel_ctx.set(True)
nested.cancel()
await nested
except asyncio.CancelledError:
print('Main task is cancelled, doing cleanup')
if __name__ == '__main__':
asyncio.run(main_task())
在上述代码中:
cancel_ctx
是一个ContextVar
,用于存储取消信号。nested_task
协程内部通过cancel_ctx.get()
检查是否收到取消信号,并在asyncio.CancelledError
异常处理中进行清理。main_task
创建并启动nested_task
,一段时间后设置取消信号并取消nested_task
,同时自身也捕获asyncio.CancelledError
异常来处理可能的取消情况。