要停止以下代码,需要按
Ctrl-C
两次,但是第二个引发的 KeyboardInterrupt
异常不会被 try-catch
方法内的内部 start_consuming
捕获。
import asyncio
async def start_consuming():
try:
await asyncio.Future()
except:
try:
await asyncio.Future()
except:
pass
async def main():
await asyncio.gather(start_consuming())
if __name__ == '__main__':
asyncio.run(main())
为什么?
事实并非如此。这是添加了一些调试打印的程序。
import asyncio
async def start_consuming():
try:
await asyncio.Future()
except BaseException as b:
print(1, repr(b))
try:
await asyncio.Future()
except BaseException as b:
print(2, repr(b))
async def main():
try:
await asyncio.gather(start_consuming())
except BaseException as b:
print(3, repr(b))
if __name__ == '__main__':
try:
asyncio.run(main())
except BaseException as b:
print (4, repr(b))
输出稍作编辑:
^C 1 CancelledError()
^C 2 CancelledError()
3 CancelledError()
4 KeyboardInterrupt()
我没有完整详细的解释,但很明显,当 ctrl-C 中断到达时,
start_consuming
协程没有执行,因为它还没有准备好。
中断(异常)随后被 asyncio 本身捕获(“捕获”)。在我看来,其他一切都是异步关闭和清理操作。然后,在退出 asyncio 期间,会通过
asyncio.run()
引发 ctrl-C 中断。
我想添加一个关于 asyncio 关闭的注释,“吞掉”取消异常 - 这也是
BaseException
- 是违反规则的。文档:“当取消异步任务时,可以捕获此异常以执行自定义操作。几乎在所有情况下,都必须重新引发异常。”