asyncio:为什么等待取消的Future不显示CancelledError?

时间:2018-07-15 06:52:28

标签: python-3.x python-asyncio

给出以下程序:

import asyncio

async def coro():
    future = asyncio.Future()
    future.cancel()
    print(future)  # <Future cancelled>
    await future  # no output


loop = asyncio.get_event_loop()
loop.create_task(coro())
loop.run_forever()

为什么CancelledError抛出的await future没有显示?将await future显式包装在try/except中表明它发生了。其他未处理的错误显示在Task exception was never retrieved中,如下所示:

async def coro2():
    raise Exception('foo')


loop.create_task(coro2())

为什么不这样等待取消的期货呢?

其他问题:如果协程等待取消的Future,内部会发生什么?它会永远等待吗?我需要做任何“清理”工作吗?

1 个答案:

答案 0 :(得分:1)

  

为什么CancelledError抛出的await future没有显示?

未显示该异常,因为您实际上从未检索过coro的结果。如果您以任何方式找回它,例如通过在任务上调用result()方法或仅等待它,您将在获取该错误的地方得到预期的错误。观察结果回溯的最简单方法是将run_forever()更改为run_until_complete(coro())

  

如果协程等待取消的Future,内部会发生什么?它会永远等待吗?我需要做任何“清理”工作吗?

它不会永远等待,它会在CancelledError处获得await。通过在await future周围添加try / except,您已经发现了这一点。您需要执行的清除与其他任何例外相同-要么什么都不做,要么使用withfinally确保在退出时释放所获取的资源。< / p>

  

Task exception was never retrieved显示其他未处理的错误[...]为什么不是这样的情况,等待取消的期货?

因为Future.cancel intentionally disables记录了追溯。这是为了避免在取消将来时产生“从未检索到异常”的输出。由于CancelledError通常是从外部注入的,并且几乎可以在任何时候发生,因此从中获取很少的价值。

如果在一个例外的情况下显示回溯听起来很奇怪,而在另一个例外的情况下显示回溯听起来很奇怪,请注意,任务的回溯显示首先要尽力而为。使用create_task创建但未等待的任务有效地在“后台”运行,就像未进行join()的线程一样。但是,与线程不同,协程具有“结果”的概念,它是从协程返回的对象或由其引发的异常。协程的返回值由其任务的result提供。当协程异常退出时,结果将保留异常,并在检索结果时自动引发异常。这就是为什么Python无法像线程由于未处理的异常而终止时那样立即打印回溯的原因-它必须等待某人实际检索结果。只有当结果为异常的Future即将被垃圾收集时,Python才能知道该异常从未被检索到并显示警告和回溯。