Jim*_*eld 3 python exception cancellation python-asyncio
我的应用程序具有以下顶级代码(略有缩写):
def main():
# Some setup code ...
try:
asyncio.run(my_coroutine())
except Exception as e:
print("Exiting due to exception {}: {}".format(type(e).__name__, e))
print("Coroutine finished")
# Some cleanup code ...
print("Shutdown complete")
Run Code Online (Sandbox Code Playgroud)
在 Python 3.8 中asyncio.run永远不会完成,因此清理代码不会运行(并且不会打印关闭文本)。当应用程序应该退出时,它只是永远挂起。在 Python 3.7 中它运行良好。Python的3.6没有asyncio.run,但相当类似的代码loop.run_until_complete()和loop.close()工作过。
一些额外的上下文:设置和清理代码启动并优雅地退出使用threading.Thread. 正是这个线程实际上停止了主协程:它取消了协程(从技术上讲,它取消了在 内调用的另一个协程my_coroutine())loop.call_soon_threadsafe
问题的前提是错误的。(我知道是在我写的时候而不是在我最初遇到问题的时候,所以我把它留在这种形式给其他有同样问题的人。)asyncio.run() 正在完成,但跳过所有异常处理和关闭代码:
def main():
# Some setup code ...
try:
asyncio.run(my_coroutine())
except Exception as e:
# <---- In Python 3.7, control passed to here when asyncio.run() finshed
print("Exiting due to exception {}: {}".format(type(e).__name__, e))
print("Coroutine finished")
# Some cleanup code ...
print("Shutdown complete")
# <---- In Python 3.8 it directly dropped out of here!
Run Code Online (Sandbox Code Playgroud)
这是因为,在 Python 3.8 中, 的基类asyncio.CancelledError从Exception变成了BaseException(它是 的基类Exception)。这样做是为了避免异步代码中的错误,因为人们Exception认为这意味着某些操作失败(例如网络错误)但意外阻止了取消;参见Python 问题 32528。
然后应用程序无法退出,因为 Python 等待所有线程完成,除非它们开始时daemon=False传递给Thread构造函数(请参阅Thread 对象文档中的几段讨论)。在我的情况下,该线程不是守护线程(因为我确实希望它优雅地完成)但除非我要求它,否则它不会退出,这是在代码中完成的,我在其中添加了注释“一些清理代码...... ”。
解决方案是asyncio.CancelledError在 之外显式捕获Exception,或者使用finally:块代替。该finally:块可能是更好的,因为它确实保证了代码将被运行,即使在源自其他异常的脸BaseException,如KeyboardInterrupt:
def main():
# Some setup code ...
try:
asyncio.run(my_coroutine())
finally:
print("Coroutine finished")
# Some cleanup code ...
print("Shutdown complete")
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
687 次 |
| 最近记录: |