【问题标题】:RuntimeError: There is no current event loop in thread 'Thread-1' , multithreading and asyncio errorRuntimeError:线程 'Thread-1' 中没有当前事件循环,多线程和异步错误
【发布时间】:2018-02-10 21:45:21
【问题描述】:

我有一个调用异步循环的线程,但这会导致上述异常:

RuntimeError: There is no current event loop in thread 'Thread-1'.

这个问题:RuntimeError: There is no current event loop in thread in async + apscheduler 遇到了同样的问题,但是他们提到了我没有的调度程序。

我的代码如下:

def worker(ws):
      l1 = asyncio.get_event_loop()
      l1.run_until_complete(ws.start())  


      l2 = asyncio.get_event_loop()
      l2.run_forever()


if __name__ == '__main__':
    ws = Server()
    p = threading.Thread(target=worker,args=(ws,))
    p.start()


    while True:
        try:
            #...do sth
        except KeyboardInterrupt:
            p.join()
            exit() 

【问题讨论】:

    标签: python multithreading python-asyncio


    【解决方案1】:

    新线程没有事件循环,因此您必须通过并显式设置它:

    def worker(ws, loop):
        asyncio.set_event_loop(loop)
        loop.run_until_complete(ws.start())
    
    if __name__ == '__main__':
        ws = Server()
        loop = asyncio.new_event_loop()
        p = threading.Thread(target=worker, args=(ws, loop,))
        p.start()
    

    另外,p.join() 不会正确终止您的脚本,因为您永远不会停止服务器,因此您的循环将继续运行,可能会挂起线程。在加入线程之前,您应该像 loop.call_soon_threadsafe(ws.shutdown) 一样调用 smth,最好等待服务器正常关闭。

    【讨论】:

    • 最好使用asyncio.new_event_loopworker 中分配一个新的事件循环并将其设置为工作线程。如下所示,将主线程的事件循环传递给worker,这可能会导致两个线程试图共享同一个事件循环实例,这将无法预料地失败。当然,上面的 sn-p 没有这个问题,但是在更大的代码库中,很容易想象有人试图例如从主线程向事件循环提交一些东西——尤其是当主线程对asyncio.get_event_loop()的调用继续返回循环时。
    • @user4815162342 你说的对,我在修改问题代码的时候要多加注意……
    • 解决了这个问题,谢谢。请注意,通过分配循环inside worker 可以使代码更加简单。这样,您根本不需要将loop 发送到worker,并且对于哪个循环属于哪个线程的任何混淆都将被删除,因为事件循环仅在内部创建(并且可能会被销毁)工人。
    • 但是你会失去对另一个线程中循环的控制吗?如果我想从主线程关闭正在运行的服务器,我该怎么办?
    • 好点。请注意,调用线程开始时几乎没有控制,因为循环在不同的线程中运行 - 例如,它甚至不能调用简单的loop.stop()(因为这样做将无法唤醒睡眠循环)。调用线程只允许调用loop.call_soon_threadsafe()asyncio.run_coroutine_threadsafe。如果需要这些,它可以创建/发送循环,但将其隐藏在仅执行这些操作的闭包中。显示这对于这个问题可能是矫枉过正,因为 OP 只触及工作人员中的事件循环,并永远运行它。
    【解决方案2】:

    我在线程中运行 Bokeh Server 时遇到了这个问题。当我尝试创建 server = Server({'/': app}, port=0) 时,我会收到此错误。从 Tornado 文档中,我发现了以下内容...

    Class tornado.platform.asyncio.AnyThreadEventLoopPolicy[source]
    

    允许在任何线程上创建循环的事件循环策略。 默认的异步事件循环策略仅在主线程中自动创建事件循环。其他线程必须显式创建事件循环,否则 asyncio.get_event_loop(因此 IOLoop.current)将失败。安装此策略允许在任何线程上自动创建事件循环,以匹配 Tornado 5.0 之前版本(或 Python 2 上的 5.0)的行为。

    用法:

    asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
    

    http://www.tornadoweb.org/en/stable/asyncio.html#tornado.platform.asyncio.AnyThreadEventLoopPolicy

    【讨论】:

      【解决方案3】:

      在 Windows 上使用 Python 3.7.3,而不是直接在线程中创建事件循环,

      我必须:

      asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
      loop = asyncio.new_event_loop()
      asyncio.set_event_loop(loop)
      

      否则问题会一直存在。

      【讨论】:

        猜你喜欢
        • 2021-02-20
        • 2018-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多