【问题标题】:asyncio.get_event_loop() throws error when FLASK_DEBUG=1当 FLASK_DEBUG=1 时 asyncio.get_event_loop() 抛出错误
【发布时间】:2020-02-26 04:15:40
【问题描述】:

我有一个烧瓶应用程序,它紧跟 fast.ai 的渲染应用程序:https://github.com/render-examples/fastai-v3。它使用 asyncio 在页面呈现时下载模型。下面的代码设置它:

loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(setup_learner())] #setup_learner downloads the model
learn = loop.run_until_complete(asyncio.gather(*tasks))[0]
loop.close()

我注意到当 FLASK_DEBUG=1 时,第一行抛出错误:

RuntimeError: 线程 'Thread-1' 中没有当前事件循环。

但是当 FLASK_DEBUG=0 时,它不会。但该应用程序更难调试。有没有人遇到过这个问题,是什么原因造成的?

【问题讨论】:

    标签: debugging flask python-asyncio


    【解决方案1】:

    这适用于FLASK_DEBUG=0 但不适用于FLASK_DEBUG=1 的原因是应用程序在非调试模式下运行在主线程中,但在调试模式下不运行在主线程中。

    相关代码在asyncio.events.BaseDefaultEventLoopPolicy.get_event_loop:

    def get_event_loop():
        if (self._local._loop is None and
                not self._local._set_called and
                isinstance(threading.current_thread(), threading._MainThread)):
            self.set_event_loop(self.new_event_loop())
    
        if self._local._loop is None:
            raise RuntimeError('There is no current event loop in thread %r.'
                               % threading.current_thread().name)
    
        return self._local._loop
    

    只能在具有当前事件循环策略的主线程中创建事件循环:在我的例子中是_UnixDefaultEventLoopPolicy。请注意,_UnixDefaultEventLoopPolicyBaseDefaultEventLoopPolicy 的子类,但不会覆盖 BaseDefaultEventLoopPolicy

    可能的解决方案:

    1. 在当前线程loop = asyncio.new_event_loop()创建一个新的事件循环
    2. 创建自定义事件循环策略,允许asyncio.get_event_loop() 通过覆盖events.AbstractEventLoopPolicy.get_event_loop 方法在主线程以外的线程中创建新的事件循环。最安全的方法可能是将您的环境使用的任何事件循环策略子类化并覆盖其get_event_loop 方法,从而删除主线程检查。这样您就可以保留所有其他政策行为,尽管更改可能会破坏其中一些行为。

    无论哪种方式,您都在为线程创建一个事件循环。我的标准倾向是简单,所以#1。

    那么为什么应用程序在调试模式下不在主线程中运行?首先,在调试模式下运行时有两个进程:Flask 服务器和调试器,分别为flask runpython pydevd.py。每个进程的主线程都有一个事件循环,它促进了应用服务器和调试器之间的通信——除其他外,还产生了应用实际运行的另一个线程。如果应用程序由多线程应用程序服务器提供服务,您还将在未启用调试模式的情况下看到此行为,例如gunicorn 或 uwsgi。

    Flask 并不真正支持asyncio。当然可以将它与 Flask 一起使用,但不能保证它们的兼容性。请参阅此issue,更具体地说:the issue referenced in its comments

    【讨论】:

    • 谢谢——您的解释确实有助于我的理解。根据您对解决方案 1 的建议: loop = asyncio.new_event_loop(); asyncio.set_event_loop(loop) 有效。
    猜你喜欢
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-29
    相关资源
    最近更新 更多