阅读时: https://docs.python.org/3/library/asyncio-task.html#asyncio.Task.cancel 捕捉cancelederror似乎有两个目的。在
一是可能会阻止任务被取消。在
另一个是确定有什么东西取消了你正在等待的任务。 如何区别?在
async def cancel_me():
try:
await asyncio.sleep(3600)
except asyncio.CancelledError:
raise
finally:
print('cancel_me(): after sleep')
async def main():
task = asyncio.create_task(cancel_me())
await asyncio.sleep(1)
task.cancel()
try:
await task
except asyncio.CancelledError:
# HERE: How do I know if `task` has been cancelled, or I AM being cancelled?
print("main(): cancel_me is cancelled now")
Asyncio不容易区分两者。当一个外部任务等待一个内部任务时,它将控制权委托给内部任务的协同进程。结果,取消这两个任务都会将一个^{} 注入到完全相同的位置:内部任务中最内部的
await
。这就是为什么您无法判断这两个任务中的哪一个最初被取消了。在但是,可以通过断开
await
的链并使用完成回调来连接任务来避免这个问题。然后在回调中截获并检测到内部任务的取消:这在功能上等同于
await task
,只是它不直接等待内部的task
;它等待一个虚拟的未来,其结果在task
完成后设置。此时,我们可以用更具体的ChildCancelled
替换CancelledError
(我们知道它一定来自内部任务的取消)。另一方面,如果外部任务被取消,它将以常规的CancelledError
形式出现在await cont
处,并像往常一样进行传播。在下面是一些测试代码:
^{pr2}$请注意,在这个实现中,取消外部任务不会自动取消内部任务,但是可以通过显式调用
child.cancel()
来轻松更改,无论是在parent
中,还是在detect_cancel
中。在Asyncio使用与implement^{} 相似的方法。在
上下文
首先,让我们考虑更广泛的背景:
caller() > your_coro() > callee()
您可以控制协同程序,但不能控制调用者,只能部分控制被调用者。在
默认情况下,“向上传播”和“向下传播”都是有效的:
在这个图中,语义和非常松散地,如果}被取消,然后你的协程被取消,然后{}被取消。如果
caller1()
被主动取消,那么{caller2()
被主动取消,也会发生大致相同的情况。在(
callee()
是共享的,因此不是一个普通的协程,而是Task
或Future
)你想要什么样的替代行为?在
屏蔽
如果您希望
^{pr2}$callee()
继续,即使caller2()
被取消,shield
它:反向屏蔽
如果您允许
callee()
死亡,但希望您的协同程序继续,请转换异常:保护自己
这个问题值得怀疑-通常你应该允许你的来电者取消你的合作计划。在
一个值得注意的例外是,如果你的调用者是一个框架,并且它是不可配置的。在
^{4}$相关问题 更多 >
编程相关推荐