Python asyncio:为什么等待一个被取消的未来不显示CanceledError?

Python asyncio:为什么等待一个被取消的未来不显示CanceledError?,python,python-3.x,python-asyncio,Python,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() 为什么

鉴于以下计划:

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()
为什么等待取消的期货不是这样

附加问题:如果一个协同程序等待一个被取消的未来,内部会发生什么?它会永远等待吗?我必须做任何“清理”工作吗

为什么
wait future
抛出的
cancelederror
未显示

不会显示异常,因为您从未实际检索过
coro
的结果。如果您以任何方式检索它,例如通过调用任务上的
result()
方法或仅通过等待它,您将在检索它的点处得到预期的错误。观察结果回溯的最简单方法是将
永远运行()
更改为
运行直到完成(coro())

如果协同程序等待被取消的未来,内部会发生什么?它会永远等待吗?我必须做任何“清理”工作吗

它不会永远等待,它会在
wait
点接收
cancelederror
。通过在
wait future
附近添加try/except,您已经发现了这一点。您需要执行的清理与任何其他异常相同-要么什么都不做,要么使用
with
finally
确保在退出时释放您获得的资源

其他未处理的错误显示为“从未检索到任务异常”[…]为什么等待取消的期货不是这种情况

因为
Future.取消
回溯日志记录。这是为了避免在取消未来时输出“exception was never retrieve”。由于
canceledError
通常是从外部注入的,并且可能(几乎)在任何时刻发生,因此从检索它中得到的值很小

如果在一个异常情况下显示回溯而在另一个异常情况下不显示回溯听起来很奇怪,请注意,失败任务的回溯从一开始就是以尽力而为的方式显示的。使用创建且未等待的任务有效地“在后台”运行,这与未使用
join()
ed的线程非常相似。但与线程不同,协同路由具有“结果”的概念,即从协同路由返回的对象或由其引发的异常。协同程序的返回值由其任务的返回值提供。当协同程序带异常退出时,结果将封装异常,在检索结果时自动引发异常。这就是为什么Python不能像线程由于未处理的异常而终止时那样立即打印回溯—它必须等待有人实际检索结果。只有当其结果包含异常的
未来的
将要进行垃圾回收时,Python才能判断结果将永远不会被检索。然后显示警告和回溯,以避免异常以静默方式传递

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


loop.create_task(coro2())