asyncio任务取消(cancel)与超时处理详解
如何在asyncio中优雅地取消任务和处理超时?task.cancel()的底层原理是什么——CancelledError如何传播?shield()保护关键操作不被取消的机制是什么?wait_for超时后底层任务是否被取消?如何捕获和处理CancelledError?
回答
我是大山
task.cancel()原理:在下一次await点向协程内抛出asyncio.CancelledError。协程需要在该异常处yield才能响应取消。
async def worker():
try:
await asyncio.sleep(100)
except asyncio.CancelledError:
print('被取消,执行清理')
raise # 必须重新抛出否则任务不会标记为取消
取消传播:取消Task时,该Task正在await的所有子协程也会收到CancelledError。
asyncio.shield():保护协程不受外部取消:
async def critical():
# 即使外层task被取消,这个操作仍会执行
await asyncio.shield(protected_coro())
wait_for超时行为:超时时内部Task被取消,并抛出asyncio.TimeoutError。
try:
result = await asyncio.wait_for(task(), timeout=5)
except asyncio.TimeoutError:
print('超时,任务已被取消')
最佳实践:
- 清理代码应在
try/finally或except CancelledError中执行 - 捕获
CancelledError后应重新抛出,除非明确不想取消 asyncio.gather(return_exceptions=True)不会自动取消,需手动检查CancelledError