как остановить () последний цикл среди нескольких вложенных циклов asyncio?

Иногда мне нужно более одной сопрограммы asyncio, в которую затем будут вложены подпрограммы: сопрограмма B, работающая в сопрограмме A, C в сопрограмме B и так далее. Проблема заключается в остановке заданного цикла. Например, использование loop.stop () в последнем верхнем цикле, таком как цикл 'C', уничтожает фактически все сопрограммы asyncio, а не только этот цикл 'C'. Я подозреваю, что stop () на самом деле убивает сопрограмму A и тем самым уничтожает все другие зависимые процедуры. Вложенные подпрограммы - call_soon_threadsafe, и все подпрограммы начинаются как run_forever.

Я пробовал использовать определенные имена циклов, или «return», или «break» (в цикле while внутри сопрограммы), но ничего не выходит из цикла, кроме stop (), который затем убивает все циклы одновременно неспецифически.

Моя проблема, которую я описал здесь, на самом деле связана с моим более ранним вопросом ... сервер демона python аварийно завершает работу во время обратного вызова наложения всплывающих окон HTML с использованием сопрограмм asyncio websocket ... которые, как я думал, я решил - пока не столкнулся с этой проблемой loop.stop ().

ниже мой пример кода для Python 3.4.3, где я пытаюсь остановить () цикл coroutine_overlay_websocket_server, как только он будет выполнен с заданием websocket. Как уже было сказано, мой код в текущем состоянии прерывает все работающие циклы. После этого 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?


person pisti    schedule 11.06.2015    source источник
comment
Можете ли вы включить какой-нибудь пример кода, демонстрирующий то, что вы описываете? Обычно независимо от того, сколько у вас сопрограмм, у вас есть только один цикл обработки событий, поэтому вызов loop.stop() приведет к закрытию всего. Похоже, вы неправильно используете фреймворк, если запускаете другой цикл для каждой сопрограммы.   -  person dano    schedule 11.06.2015
comment
Обычно вам не нужно несколько циклов в одном потоке. Просто позвоните loop.run_forever() и пусть он сделает всю работу. Зачем вам нужно несколько run_until_complete в одном цикле (get_event_loop() возвращает существующий, а не создает новый)?   -  person Andrew Svetlov    schedule 12.06.2015


Ответы (1)


Меня все еще немного смущает то, что вы пытаетесь сделать, но определенно нет необходимости пытаться вкладывать циклы событий - ваша программа однопоточная, поэтому, когда вы вызываете asyncio.get_event_loop() несколько раз, вы всегда получите тот же цикл событий. Таким образом, в вашем примере вы действительно не создаете два разных цикла; оба fmDaemon.run и Function1 используют один и тот же. Вот почему остановка loop внутри Function1 также убивает сопрограмму, запущенную вами внутри 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.
person dano    schedule 12.06.2015
comment
Здесь - немного поздно - отзывы и большое спасибо Дано, Ласло Мараи из CodeMentor и другим. Решение заключалось в том, чтобы использовать в каждой ссылке yield from function1, yield from function2 и т. Д. Всякий раз, когда вызов asyncio.coroutine существует в самой последней functionN в конце цепочки вызовов функций. Вот и все: каскад вызовов функций разворачивается безупречно. - person pisti; 26.08.2015