正在取消一个正在等待任务取消的任务。它是如何工作的?

2024-04-24 10:36:01 发布

您现在位置: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+

输出(省略回溯):

^{pr2}$

Tags: selfloopasynciotaskdeftimeoutawaitreply
1条回答
网友
1楼 · 发布于 2024-04-24 10:36:01

I prepared a MCVE and noticed, that a future that the task is waiting for gets cancelled when the task is cancelled. This suits me fine, it could be a base for a solution, but I do not understand why that future gets cancelled and if I can rely on it.

是的,如果任务等待未来,则该未来将被取消。未来可能是另一个任务,因此取消将蔓延到最底层的未来,等待。它的实现makes sure,但文档没有明确说明。在

我会继续依赖这种行为,原因有二:

  • 在这一点上,如果不在向后兼容性方面造成重大破坏,就不可能更改它。开发人员已经拒绝了smaller更改,因为它们会破坏现有代码。

  • 没有其他方法可以实现这一点而不会导致资源泄漏。如果您要取消的任务正在等待将来,除了取消它,您会做什么?如果你只是让它在后台运行,你就有可能永远保持它的存在,因为未来可能永远不会独立存在。如果通过从调度程序中删除它来“修复”这个问题(同样,没有取消),未来将永远没有机会清理它获得的资源,这肯定会导致资源泄漏。

因此,依赖于取消向下传播是安全的,但用asyncio.shield()屏蔽的期货除外,这是为意味着在后台继续运行并拥有自己的生命周期管理的期货保留。在

相关问题 更多 >