Переменная в асинхронной функции не переоценивается в цикле while-True - PullRequest
0 голосов
/ 07 июня 2018

Я сделал фиктивный сервер для проверки моего приложения websockets.Он прослушивает subscription сообщения, затем выдает информацию об этих подписках через сокет.

Атрибут класса 'subscriptions пуст при инициализации и должен заполняться, поскольку функция listen() получает сообщения подписки.Однако кажется, что self.subscriptions в talk() никогда не добавляется, оставляя его застрявшим в бесконечном цикле while и никогда не передавая сообщения.

Проблема решается добавлением строки await asyncio.sleep(1) последля цикла.Но почему?Разве self.subscriptions не следует пересматривать каждый раз при запуске цикла for?

Код ниже:

class DummyServer:
    def __init__(self):
        self.subscriptions = []

    def start(self):
        return websockets.serve(self.handle, 'localhost', 8765)

    async def handle(self, websocket, path):
        self.ws = websocket
        listen_task = asyncio.ensure_future(self.listen())
        talk_task = asyncio.ensure_future(self.talk())

        done, pending = await asyncio.wait(
            [listen_task, talk_task],
            return_when=asyncio.FIRST_COMPLETED
        )

        for task in pending:
            task.cancel()

    async def listen(self):
        while True:
            try:
                msg = await self.ws.recv()
                msg = json.loads(msg)
                await self.triage(msg)  # handles subscriptions
            except Exception as e:
                await self.close()
                break

    async def talk(self):
        while True:
            for s in self.subscriptions:
                dummy_data = {
                    'product_id': s
                }
                try:
                    await self.send(json.dumps(dummy_data))
                except Exception as e:
                    await self.close()
                    break

            await asyncio.sleep(1)  # without this line, no message is ever sent

1 Ответ

0 голосов
/ 08 июня 2018

В начале вашей функции, subscriptions пусто, а тело for не оценивается.В результате ваша сопрограмма практически совпадает с:

async def talk(self):
    while True:
        pass

Цикл while не содержит «точки переключения контекста», что означает, что цикл событий asyncio в основном зависает там, навсегда выполняя блокирует цикл while.

Добавление await sleep() разрывает магический круг;даже await sleep(0) может помочь.

Умный код, вероятно, должен использовать asyncio.Condition в сочетании с self.subscriptions, но этот вопрос выходит за рамки вашего исходного вопроса.

...