【问题标题】:how to stop() the last loop among multiple nested asyncio loops?如何停止()多个嵌套异步循环中的最后一个循环?
【发布时间】:2015-08-26 11:30:18
【问题描述】:

我有时需要多个 asyncio 协程,然后这些例程将被嵌套:协程 B 在协程 A 中运行,C 在 B 中运行,依此类推。问题在于停止给定的循环。例如,在最后一个顶部循环(例如循环'C')中使用 loop.stop() 实际上会杀死所有异步协程 - 而不仅仅是这个循环'C'。我怀疑 stop() 实际上会杀死协程 A,并且这样做会消灭所有其他依赖的例程。嵌套的例程是 call_soon_threadsafe,所有例程都以 run_forever 开头。

我尝试使用特定的循环名称,或“return”或“break”(在协程内的 while 循环中),但没有任何内容退出循环 - 除了 stop(),它会立即杀死非特定的所有循环。

我在这里描述的问题实际上与我之前的一个问题有关...... python daemon server crashes during HTML popup overlay callback using asyncio websocket coroutines ...我以为我已经解决了 - 直到遇到这个 loop.stop() 问题。

下面是我的 Python 3.4.3 示例代码,我尝试在完成 websocket 作业后立即停止() coroutine_overlay_websocket_server 循环。如前所述,我当前状态的代码会中断所有正在运行的循环。此后 fmDaemon 重新创建了一个新的 asyncio 循环,它对之前的计算一无所知:

import webbrowser
import websockets
import asyncio

class fmDaemon( Daemon):

    # Daemon - see : http://www.jejik.com/articles/2007/02/a_simple_unix_linux_daemon_in_python/
    # Daemon - see : http://www.jejik.com/files/examples/daemon3x.py

    def __init__( self, me):
        self.me = me

    def run( self):

        while True:

            @asyncio.coroutine
            def coroutine_daemon_websocket_server( websocket, path):

                msg = yield from websocket.recv()

                if msg != None:
                    msg_out = "{}".format( msg)
                    yield from websocket.send( msg_out)
                    self.me = Function1( self.me, msg)

            loop = asyncio.get_event_loop()
            loop.run_until_complete( websockets.serve( coroutine_daemon_websocket_server, self.me.IP, self.me.PORT))
            loop.run_forever()


def Function1( me, msg):

    # doing some stuff :
    # creating HTML file,
    # loading HTML webpage with a webbrowser call,
    # awaiting HTML button press signal via websocket protocol :

    @asyncio.coroutine
    def coroutine_overlay_websocket_server( websocket, path):

        while True:
            msg = yield from websocket.recv()
            msg_out = "{}".format( msg)
            yield from websocket.send( msg_out)

            if msg == 'my_expected_string':
                me.flags['myStr'] = msg
                break

        loop.call_soon_threadsafe( loop.stop)

    loop = asyncio.get_event_loop()
    loop.call_soon_threadsafe( asyncio.async, websockets.serve( coroutine_overlay_websocket_server, me.IP, me.PORT_overlay))
    loop.run_forever()
    loop.call_soon_threadsafe( loop.close)

    # program should continue here...

我的两个问题:1)有没有办法退出给定的协程而不会杀死较低的协程? 2) 或者,您知道不使用 asyncio 的读取 websocket 调用的方法吗?

【问题讨论】:

  • 您能否提供一些示例代码来演示您所描述的内容?通常,无论您有多少协程,您都只有一个事件循环,因此调用 loop.stop() 将关闭一切。如果您为每个协程启动不同的循环,听起来您可能误用了框架。
  • 通常你不需要在同一个线程中有多个循环。只需致电loop.run_forever(),让他完成所有工作。为什么在同一个循环中需要多个run_until_completeget_event_loop() 返回现有的,不创建新的)?

标签: python loops python-asyncio halt


【解决方案1】:

我仍然对您尝试做的事情感到有些困惑,但绝对没有必要尝试嵌套事件循环 - 您的程序是单线程的,所以当您多次调用 asyncio.get_event_loop() 时,您' re 总是会得到相同的事件循环。因此,您实际上并没有在示例中创建两个不同的循环; fmDaemon.runFunction1 都使用同一个。这就是为什么在 Function1 内停止 loop 也会杀死您在 run 内启动的协程。

也就是说,没有理由一开始就尝试创建两个不同的事件循环。 Function1 被一个协程调用,并且想调用其他协程,那为什么不把它也变成一个协程呢?然后你可以直接调用yield from websockets.serve(...),然后使用asyncio.Event等待coroutine_overlay_websocket_server完成:

import webbrowser
import websockets
import asyncio

class fmDaemon( Daemon):
    def __init__( self, me):
        self.me = me

    def run( self):
        @asyncio.coroutine
        def coroutine_daemon_websocket_server(websocket, path):
            msg = yield from websocket.recv()

            if msg != None:
                msg_out = "{}".format( msg)
                yield from websocket.send( msg_out)
                self.me = Function1( self.me, msg)

        loop = asyncio.get_event_loop()
        loop.run_until_complete(websockets.serve(coroutine_daemon_websocket_server, 
                                                 self.me.IP, 
                                                 self.me.PORT))
        loop.run_forever()


@asyncio.coroutine
def Function1(me, msg):
    @asyncio.coroutine
    def coroutine_overlay_websocket_server(websocket, path):

        while True:
            msg = yield from websocket.recv()
            msg_out = "{}".format( msg)
            yield from websocket.send( msg_out)

            if msg == 'my_expected_string':
                me.flags['myStr'] = msg
                break

        event.set() # Tell the outer function it can exit.

    event = asyncio.Event()
    yield from websockets.serve(coroutine_overlay_websocket_server, me.IP, me.PORT_overlay))
    yield from event.wait() # This will block until event.set() is called.

【讨论】:

  • 这里 - 有点晚了 - 一些反馈,非常感谢 CodeMentor 的 Dano、László Marai 和其他人。解决方案是在函数调用链末尾的最后一个函数N 中存在asyncio.coroutine 调用时,在每个链接中使用“函数1 的生成”、“函数2 的生成”等。就是这样:函数调用的级联完美地展开。
猜你喜欢
  • 2021-12-23
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 2020-01-03
  • 1970-01-01
  • 1970-01-01
  • 2018-07-29
  • 2021-09-15
相关资源
最近更新 更多