python - 为什么 `asyncio.run()` 在 Python 3.8 中永远不会返回?

标签 python exception python-asyncio cancellation

我的应用程序具有以下顶级代码(略有缩写):

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 的基类原为 ExceptionBaseException (这是 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/

相关文章:

python - 如何阻止函数在事件循环中完成其代码

python - 为什么我在 Windows 上使用 async 和 await 时会出现 NotImplementedError?

python - 使用时间戳列表选择由 DatetimeIndex 索引的 Pandas DataFrame 的子集

wpf - "The operation completed successfully"异常

安卓|在 bundle 中使用 Parcelable 数据时解码未知类型

python - 设计代码以在 Django/Python 应用程序中正确处理错误?

python-asyncio - 使用 Sphinx autodoc 记录 Python 协程

python - 带 PIL 的彩色文本

python - Graphene Mutation错误,字段必须是映射(dict/OrderedDict)

python - 如何在 SQL 中使用 'in' 和 'not in' 过滤 Pandas 数据帧