【问题标题】:Why does `asyncio.run()` never return in Python 3.8?为什么 `asyncio.run()` 在 Python 3.8 中永远不会返回?
【发布时间】:2020-05-26 18:59:51
【问题描述】:

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

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 创建的工作线程。正是这个线程真正停止了主协程:它使用loop.call_soon_threadsafe取消了协程(从技术上讲,它取消了在my_coroutine()中调用的另一个协程)

【问题讨论】:

    标签: python exception python-asyncio cancellation


    【解决方案1】:

    问题的前提是错误的。 (我知道在我写的时候,但不是在我最初遇到问题的时候,所以我把它留给其他有同样问题的人。)asyncio.run() is 完成,但跳过所有异常处理和关闭代码:

    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 中的几段讨论)启动。就我而言,该线程不是守护线程(因为我确实希望它优雅地完成),但除非我要求它退出,否则它不会退出,这是在我放置注释“一些清理代码...... ”。

    解决方案是在Exception 之外显式捕获asyncio.CancelledError,或者改用finally: 块。 finally: 块可能更好,因为它确实保证了代码会运行,即使面对来自BaseException 的其他异常,例如KeyboardInterrupt

    def main():
        # Some setup code ...
        try:
            asyncio.run(my_coroutine())
        finally:
            print("Coroutine finished")
            # Some cleanup code ...
            print("Shutdown complete")
    

    【讨论】:

    • 我有这个问题,没看懂,怎么让asyncio.run退出?没有取消异常asyncio.run(asyncio.sleep(3)) #never returns
    猜你喜欢
    • 1970-01-01
    • 2016-02-08
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-15
    相关资源
    最近更新 更多