给出以下程序:
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,内部会发生什么?它会永远等待吗?我需要做任何“清理”工作吗?
答案 0 :(得分:1)
为什么
CancelledError
抛出的await future
没有显示?
未显示该异常,因为您实际上从未检索过coro
的结果。如果您以任何方式找回它,例如通过在任务上调用result()
方法或仅等待它,您将在获取该错误的地方得到预期的错误。观察结果回溯的最简单方法是将run_forever()
更改为run_until_complete(coro())
。
如果协程等待取消的Future,内部会发生什么?它会永远等待吗?我需要做任何“清理”工作吗?
它不会永远等待,它会在CancelledError
处获得await
。通过在await future
周围添加try / except,您已经发现了这一点。您需要执行的清除与其他任何例外相同-要么什么都不做,要么使用with
和finally
确保在退出时释放所获取的资源。< / p>
用
Task exception was never retrieved
显示其他未处理的错误[...]为什么不是这样的情况,等待取消的期货?
因为Future.cancel
intentionally disables记录了追溯。这是为了避免在取消将来时产生“从未检索到异常”的输出。由于CancelledError
通常是从外部注入的,并且几乎可以在任何时候发生,因此从中获取很少的价值。
如果在一个例外的情况下显示回溯听起来很奇怪,而在另一个例外的情况下显示回溯听起来很奇怪,请注意,任务的回溯显示首先要尽力而为。使用create_task
创建但未等待的任务有效地在“后台”运行,就像未进行join()
的线程一样。但是,与线程不同,协程具有“结果”的概念,它是从协程返回的对象或由其引发的异常。协程的返回值由其任务的result提供。当协程异常退出时,结果将保留异常,并在检索结果时自动引发异常。这就是为什么Python无法像线程由于未处理的异常而终止时那样立即打印回溯的原因-它必须等待某人实际检索结果。只有当结果为异常的Future
即将被垃圾收集时,Python才能知道该异常从未被检索到并显示警告和回溯。