MST

星途 面试题库

面试题:Go语言Context如何在多协程间传递取消信号

假设你有一个包含多个嵌套协程调用的复杂任务,如何通过Context来实现当外部发起取消请求时,所有相关协程都能及时收到取消信号并进行清理工作,请详细描述实现思路并给出关键代码片段。
42.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 引入上下文对象:使用contextlib.Context相关工具,如asyncio.CancelledErrorcontextvars.ContextVarasyncio.CancelledError是协程被取消时抛出的异常,我们可以在协程内部捕获这个异常来进行清理工作。ContextVar可以在不同的协程之间传递上下文信息,包括取消信号。
  2. 传递上下文:在创建和调用嵌套协程时,将上下文对象作为参数传递,这样所有协程都能感知到取消信号。
  3. 捕获取消信号:在每个协程内部,捕获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())

在上述代码中:

  1. cancel_ctx是一个ContextVar,用于存储取消信号。
  2. nested_task协程内部通过cancel_ctx.get()检查是否收到取消信号,并在asyncio.CancelledError异常处理中进行清理。
  3. main_task创建并启动nested_task,一段时间后设置取消信号并取消nested_task,同时自身也捕获asyncio.CancelledError异常来处理可能的取消情况。