无法通过cancel函数取消asyncio.task的问题求助
问题描述
期望通过cancel函数取消foo_wrapper对应的任务,但任务仍会生成结果。若将foo_wrapper改为非异步函数,并将self.lock替换为threading.Lock则可解决问题,请问这是什么原因?
原代码
import asyncio from typing import List, Optional class Bar: def __init__(self): self.tasks = set() self.lock = asyncio.Lock() async def foo(self) -> str: # Simulate some asynchronous operation await asyncio.sleep(2) return "response" async def foo_wrapper(self) -> str: task = asyncio.create_task(self.foo()) async with self.lock: self.tasks.add(task) return task def cancel(self): for task in self.tasks: print(f'cancel {task}') print(task.cancel()) async def main(): bar = Bar() c = bar.foo_wrapper() bar.cancel() try: print(await (await c)) except asyncio.CancelledError: print('Canceled 2') if __name__ == "__main__": asyncio.run(main())
原因分析
1. 异步函数foo_wrapper的执行时机问题
调用c = bar.foo_wrapper()时,并不会立即执行foo_wrapper内部代码——异步函数调用仅返回一个协程对象,必须通过await触发执行。而你调用bar.cancel()时,foo_wrapper还未运行,self.tasks是空集合,cancel根本找不到要取消的任务。直到后续执行await (await c),foo_wrapper才会被执行,此时cancel已经执行完毕,完全错过了取消时机。
2. asyncio.Lock与同步场景的不匹配
asyncio.Lock是专为异步代码设计的锁,必须在异步上下文(async def函数内部,配合await或async with)中使用。不过这里核心问题并非锁本身,即使去掉async with self.lock,原代码的问题依然存在,根源还是异步函数的执行顺序问题。
3. 改为非异步函数+threading.Lock生效的原因
- 改为非异步函数后,调用
bar.foo_wrapper()会立即同步执行内部代码:创建foo的任务、通过threading.Lock(同步锁,可在普通函数中直接使用)将任务加入self.tasks集合,再返回任务对象。 - 此时调用
bar.cancel(),self.tasks已包含目标任务,cancel()能成功标记任务为取消状态。后续await该任务时,asyncio.sleep(2)会检测到取消请求,抛出CancelledError,任务被成功取消。
额外优化建议
如果想保留异步的foo_wrapper,可以调整执行顺序,确保任务先加入集合再调用cancel:
async def main(): bar = Bar() # 先await foo_wrapper,确保任务已加入集合 task = await bar.foo_wrapper() bar.cancel() try: print(await task) except asyncio.CancelledError: print('Canceled 2')
内容的提问来源于stack exchange,提问作者flexwang
相关产品推荐
相关产品推荐

