python - 取消任务会取消该任务正在等待的未来。它是如何工作的?
问题描述
我有一个实现请求/回复事务的等待对象。如果事务超时,它将在放弃并引发异常之前重试几次。
现在假设它总是超时,因为这是我遇到问题的情况。
当任务开始此操作然后被取消时,重试将继续。这不是我想要的。我想完全取消操作。
我准备了一个 MCVE 并注意到,当任务被取消时,任务正在等待的未来会被取消。这很适合我,它可能是解决方案的基础,但我不明白为什么那个未来会被取消,以及我是否可以依赖它。
import asyncio
RETRIES = 2
TIMEOUT = 1.0
class ClientRPC:
def __init__(self):
self._reply = None
self._retries = RETRIES
def __await__(self):
self.start()
return self._reply.__await__()
def start(self):
loop = asyncio.get_event_loop()
if self._reply is None:
self._reply = loop.create_future()
loop.call_later(TIMEOUT, self.handle_timeout)
# send a request
print("REQUEST")
def handle_timeout(self):
print("TIMEOUT")
print("future", repr(self._reply._state))
if self._retries > 0:
self._retries -= 1
self.start()
else:
self._reply.set_exception(RuntimeError("Timeout!"))
def handle_reply(self, reply):
# unused in this example
pass
async def client():
transaction = ClientRPC()
try:
reply = await transaction
except asyncio.CancelledError:
print("--CANCELLED--")
async def test():
loop = asyncio.get_event_loop()
task = loop.create_task(client())
await asyncio.sleep(1.5)
task.cancel()
await asyncio.sleep(3)
asyncio.run(test()) # python 3.7+
输出(省略回溯):
要求 超时 未来的“待定” 要求 - 取消 - 超时 未来“取消”<--为什么? 要求 超时 未来“取消” 回调 ClientRPC.handle_timeout() 中的异常 处理: asyncio.base_futures.InvalidStateError:无效状态
解决方案
我准备了一个 MCVE 并注意到,当任务被取消时,任务正在等待的未来会被取消。这很适合我,它可能是解决方案的基础,但我不明白为什么那个未来会被取消,以及我是否可以依赖它。
是的,如果任务等待未来,该未来将被取消。该未来可能是另一项任务,因此取消将传播到等待的最底层未来。实现可以确保这一点,但文档没有明确说明。
我会继续依赖这种行为,原因有两个:
在不严重破坏向后兼容性的情况下,此时不可能对其进行更改。开发人员已经拒绝了较小的更改,因为它们会破坏现有代码。
没有其他方法可以实现这不会导致资源泄漏。如果你正在取消的任务正在等待一个未来,除了取消它你会做什么?如果您只是让它在后台运行,您可能会永远保留它,因为未来可能永远不会自行退出。如果只是通过从调度程序中删除它来“修复”它(同样,没有取消),未来将永远没有机会清理它获得的资源,这肯定会导致资源泄漏。
因此,依赖于向下传播的取消是安全的,除了用 屏蔽的期货asyncio.shield()
,它保留给旨在保持在后台运行并拥有自己的生命周期管理的期货。
推荐阅读
- c# - 需要在 Web Api 中的 URL 中作为整数传递的字符串字段
- batch-file - 如何在 .bat 文件中设置具有身份验证的 Internet 代理?
- python - Python中的模板设计模式关于覆盖模板方法的问题
- java - 从具有重复性的排序数组创建未排序集时
- javascript - 用空对象反应原生 setState 更新数组
- go - 尝试初始化 2D 切片,但元素引用更改了切片中的所有行
- regex - 匹配两个或多个不相同的字符
- java - 如何使用 Mockito 或 PowerMockito 在 JUnit 中模拟包含文件和 BufferedReader 的方法
- c# - 两个类库都需要相互引用
- python - 如何在 Django 中使用自定义模型扩展自用户模型?