python异步任务取消是协作式中断,需协程主动响应cancellederror异常;cancel()仅设状态并下一次await时抛出该异常,纯cpu计算或捕获不重抛会导致取消失效。

Python 的异步任务取消不是“立刻终止”,而是通过协作式中断实现的——任务需主动检查取消信号并自行退出。核心在于 asyncio.Task 的 cancel() 方法触发 CancelledError 异常,而该异常只有在任务下一次 await 时才会被抛出。
取消的本质:抛出 CancelledError 并等待协程响应
调用 task.cancel() 不会强行杀掉协程,只是将任务状态设为“已取消”,并在其下一次挂起点(如 await asyncio.sleep(1)、await reader.read())注入 CancelledError。若协程未 await 任何可取消的 awaitable,或捕获了异常却不重新抛出,取消就会“失效”。
- 必须在
await表达式处才能中断;纯 CPU 计算(如for i in range(10**8))会阻塞取消 -
try/except CancelledError后若不raise,任务会继续运行,看似“取消失败” - 使用
asyncio.shield()包裹的协程无法被外部取消(除非内部也主动响应)
安全取消的典型模式
编写可取消协程时,应显式处理取消逻辑,尤其在清理资源或退出循环时:
- 在长循环中定期
await asyncio.sleep(0)或await asyncio_yield()(自定义零延迟挂起点),让取消信号有机会进入 - 用
async with管理异步资源(如连接、锁),确保__aexit__在取消时仍能执行 - 捕获
CancelledError后完成必要清理,再raise向上传播(除非明确要抑制取消)
示例:
立即学习“Python免费学习笔记(深入)”;
在Android中实现异步任务机制有两种方式,Handler和AsyncTask。本文档主要讲述的是详解Android中AsyncTask的使用;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
async def fetch_with_timeout(url, timeout=5):
try:
async with aiohttp.ClientSession() as session:
async with session.get(url, timeout=timeout) as resp:
return await resp.text()
except asyncio.CancelledError:
print("任务被取消,正在清理...")
# 这里可关闭未完成的流、释放锁等
raise # 必须重抛,否则取消被吞批量取消与超时控制
实际开发中,常需对一组任务统一管理:
-
asyncio.wait(tasks, timeout=3.0)可设定整体超时,超时后未完成的任务仍处于 pending 状态,需手动 cancel -
asyncio.create_task()创建的任务建议保存引用,便于后续取消;也可用asyncio.all_tasks()获取当前所有任务(慎用于生产) - 推荐组合使用
asyncio.timeout()(Python 3.11+)或asyncio.wait_for()实现带自动取消的超时逻辑
例如:
try:
async with asyncio.timeout(2.0):
result = await fetch_data()
except TimeoutError:
print("请求超时,已自动取消")常见陷阱与规避方法
取消机制易被误用,导致行为不符合预期:
-
同步阻塞阻断取消:避免在协程中调用
time.sleep()、requests.get()等同步阻塞函数;改用await asyncio.sleep()或异步客户端 -
忽略返回值的 await:如
await asyncio.create_task(...)会等待子任务完成,失去并发性;应先创建再统一 await 或 cancel - 未 await 的 task 对象被 GC:若创建 task 后未保存引用也未 await,可能被垃圾回收,导致取消失效或资源泄漏
不复杂但容易忽略。









