如何在python asyncio中等待select.select调用

时间:2018-01-14 10:16:03

标签: python sockets python-asyncio

我有一个python 3.6程序,我正在使用asyncio包事件循环。我的一个数据源来自api,它不是围绕asyncio构建的。我的连接对象包含一个名为_connection的成员,它只是一个python套接字。现在我可以在select语句中使用它来判断数据何时准备就绪。

async def run(self):
    while True:
        if select.select([self._q._connection], [], [])[0]:
            msg = self._q.receive()
            print(msg)

我真正想要的是......

async def run(self):
    while True:
        if await select.select([self._q._connection], [], [])[0]:
            msg = self._q.receive()
            print(msg)

我知道asyncio事件循环中有一个sock_recv函数但是我需要api来进行实际的读取和解码。我尝试了这个,但它只会落在我认为有意义的等待,因为我说的是0字节。

async def run(self):
    while True:
        print('A')
        await asyncio.get_event_loop().sock_recv(self._q._connection, 0)
        print('B')
        msg = self._q.receive()
        print(msg)

我现在能想到的唯一解决方案是在select中添加一个小超时,然后在没有数据的情况下调用asyncio.sleep,但这似乎是一种无效的方法。我希望有asyncio.select之类的东西。有人想推荐另一种方法吗?

编辑:现在我想出了这个。我不喜欢它,因为它增加了额外的四分之一秒延迟(对我的应用程序来说可能并不重要,但它仍然让我感到困扰。)

async def run(self):
    while True:
        if select.select([self._q._connection], [], [], 0)[0]:
           print(self._q.receive())
        else:
            await asyncio.sleep(0.25)

1 个答案:

答案 0 :(得分:6)

您可以使用loop.add_reader等待套接字的读取可用性:

async def watch(fd):
    future = asyncio.Future()
    loop.add_reader(fd, future.set_result, None)
    future.add_done_callback(lambda f: loop.remove_reader(fd))
    await future

async def run(self):
    while True:
        await watch(self._q._connection)
        msg = self._q.receive()
        print(msg)

但是,避免所提到的库的所有阻塞IO调用而不完全重写它将是非常棘手的。相反,我建议使用loop.run_in_executor方法来安排线程池中的阻塞IO调用:

async def run(self):
    loop = asyncio.get_event_loop()
    while True:
        msg = await loop.run_in_executor(None, self._q.receive)
        print(msg)