我的应用程序具有以下顶级代码(略有缩写):
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")
在 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!
这是因为,在 Python 3.8 中,
asyncio.CancelledError
的基类原为 Exception
至 BaseException
(这是 Exception
的基类)。这样做是为了避免人们在异步代码中发现错误 Exception
认为这意味着某些操作失败(例如网络错误)但意外阻止了取消;见 Python issue 32528 .然后应用程序无法退出,因为 Python 等待所有线程完成,除非它们以
daemon=False
启动。传递给 Thread
构造函数(参见 Thread objects docs 的几段讨论)。在我的情况下,该线程不是守护线程(因为我确实希望它优雅地完成)但除非我要求它,否则它不会退出,这是在代码中完成的,我在其中添加了注释“一些清理代码...... ”。解决办法是要么抓
asyncio.CancelledError
明确除了 Exception
,或使用 finally:
块代替。 finally:
block 可能更好,因为它确实保证代码将运行,即使面对源自 BaseException
的其他异常也是如此。如 KeyboardInterrupt
:def main():
# Some setup code ...
try:
asyncio.run(my_coroutine())
finally:
print("Coroutine finished")
# Some cleanup code ...
print("Shutdown complete")
关于python - 为什么 `asyncio.run()` 在 Python 3.8 中永远不会返回?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60167683/