异步函数中的变量不会在while-True循环中重新计算

时间:2018-06-07 17:24:52

标签: python websocket python-asyncio

我做了一个虚拟服务器来测试我的websockets应用程序。它侦听subscription消息,然后通过套接字提供有关这些订阅的信息。

类'subscriptions属性在初始化时为空,应该填充为listen()函数接收订阅消息。但是,好像self.subscriptions中的talk()似乎永远不会附加到其中,而是留在无限的while循环中,永远不会传输消息。

通过在f​​or循环之后添加一行await asyncio.sleep(1)来解决问题。但为什么?每次启动for循环时,是否不应重新评估self.subscriptions

以下代码:

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 :(得分:3)

在您的函数开始时,subscriptions为空,并且不评估for正文。因此,您的协程几乎与以下内容相同:

async def talk(self):
    while True:
        pass

while循环不包含"上下文切换点",这意味着asyncio事件循环基本上挂起,永远执行阻塞 while循环。

添加await sleep()打破了魔术圈;即使await sleep(0)也可以提供帮助。

聪明的代码可能应该将asyncio.Conditionself.subscriptions结合使用,但这个问题超出了原始问题的范围。