Python asyncio future add_done_callback然后取消未来

时间:2018-01-16 12:40:17

标签: python python-asyncio

我有一个问题需要了解asyncio的工作原理 如果我创建了未来future = asyncio.Future(),则添加add_done_callback(done_callback),之后取消未来 future.cancel() done_callback不应该被解雇? 我尝试使用loop.run_forever(),但我最终得到了无限循环。

我有一个小示例代码:

_future_set = asyncio.Future()


def done_callback(f):
    if f.exception():
        _future_set.set_exception(f.exception)
    elif f.cancelled():
        _future_set.cancel()
    else:
        _future_set.set_result(None)

async def check_stats(future):
    while not future.cancelled():
        print("not done")
        continue
    loop.stop()

def set(future):
    if not _future_set.done():
        future.add_done_callback(done_callback)


loop = asyncio.new_event_loop()
future = loop.create_future()

asyncio.ensure_future(check_stats(future), loop=loop)

set(future)
future.cancel()
loop.run_forever()  # infinite
print(_future_set.cancelled())

我知道缺少某些东西,也许这不是行为,但我会很高兴在这里得到一点帮助。

我正在使用python 3.6

**更新 设置为fire后,我将add_done_callback绑定到将来 当我取消未来并且将来的状态更改为已取消并完成时,我希望_future_set也将被取消。 并且print(_future_set.cancelled())将为True

1 个答案:

答案 0 :(得分:1)

来自docstring(在我的unix系统上)help(loop.run_forever)

  run_forever() method of asyncio.unix_events._UnixSelectorEventLoop instance
    Run until stop() is called.

当你致电loop.run_forever()时,在ioloop实例上调用stop()之前,程序不会超过该行,并且代码中没有任何内容可以这样做。

loop.run_forever()基本上是这样做的:

def run_forever(self):
    while not self.stopped:
        self.check_for_things_to_do()

如果不了解您想要达到的目标,那么很难帮助您。但是,您似乎期望loop.run_forever()在执行python代码时是异步的,但事实并非如此。 IOLoop将继续循环并检查filevents并在期货上触发回调,并且只有在被告知停止循环时才会返回到它所调用的点。

啊,我现在意识到你期待发生的事情。您需要通过future = loop.create_future()future = asyncio.Future(loop=loop)向ioloop注册期货。前者是创造未来的the preferred method N.B。代码仍然会在loop.run_forever()调用时永远运行,除非它被停止,因此仍然无法访问您的print语句。

进一步补充:如果您实际运行了问题中的代码,则f.exception()会引发异常,as per the docs

  

<强>例外()

     

返回在此未来设置的异常。

     

仅在未来完成时才返回异常(如果未设置异常,则返回None)。如果未来被取消,则引发CancelledError。如果未来尚未完成,则引发InvalidStateError。

这意味着done_callback()的调用将在第一个if f.exception()停止。因此,如果您将done_callback()切换为:

def done_callback(f):
    if f.cancelled():
        _future_set.cancel()
    elif f.exception():
        _future_set.set_exception(f.exception)
    else:
        _future_set.set_result(None)

然后你得到预期的输出。