LINUX.ORG.RU

Called while another coroutine is already waiting for incoming data

 , , ,


0

1

Приветствую!

Пишу на питоне интеграцию одного девайса с сервером умного дома home-assistant. Там повсюду asyncio и вечно сталкиваюсь с гонкой доступа к TCP-соединению.

Обмен данными с девайсом происходит в виде текстового чатика по одному TCP-соединению, а стиль програмирования под homeassistant заставляет вкидывать в eventloop таски, не дожидаясь завершения их выполнения..

Условно говоря, есть метод

async def send_command(self, cmd):
    self._cmd_wrter.write(cmd + b'\r\n')
    await self._cmd_wrter.drain()
    buf: bytes = await self._cmd_reader.readline()
    ...

Но, из-за специфики homeassistant, работать с этим методом приходится без await. Грубо говоря:

def callback():
    asyncio.ensure_future(device.send_command("SOME COMMAND"))

callbacks = [callback, callback]

for callback in callbacks:
    callback()

Вылетает ошибка readuntil() called while another coroutine is already waiting for incoming data. Я так понимаю, из-за попытки одновременного вызова readline() из нескольких корутин

Подскажите, что тут можно поделать? Может как-то переписать send_command, чтобы она обрабатывала все запросы последовательно? Хотя вроде перед readline() и так стоит await.. Не могу сообразить.

а стиль програмирования под homeassistant заставляет вкидывать в eventloop таски, не дожидаясь завершения их выполнения..

Почему так? Либо await таски, либо loop.run_until_complete, либо сооруди какую-нибудь очередь.

Либо вот так:

import asyncio

async def send_transaction(reader, writer, lock):

    
    async with lock:
        writer.write('aa\n'.encode('utf-8'))
        buf = await reader.readline()
        print(buf)


async def main(loop):
    print('Starting main')
    reader, writer = await asyncio.open_connection('127.0.0.1', 3000, loop=loop)
    lock = asyncio.Lock()
    tasks = []
    for i in range(3):
        tasks.append(asyncio.create_task(send_transaction(reader, writer, lock)))
    results = await asyncio.gather(*tasks)
    print('>DONE')

if __name__ == '__main__':
    event_loop = asyncio.get_event_loop()
    try:
        event_loop.run_until_complete(main(event_loop))
    finally:
        event_loop.close()
pawnhearts ★★★★ ()
Последнее исправление: pawnhearts (всего исправлений: 1)
Ответ на: комментарий от pawnhearts

Либо вот так

Спасибо. Просплюсь и прочту)

Почему так?

Разные виды утройств загружаются при помощи функции async_load_platform. В нем написано Warning: Do not await this inside a setup method to avoid a dead lock. Use `hass.async_create_task(async_load_platform(..))` instead."

И когда я в цикле создаю несколько таких тасков, они в конечном счете, начинают параллельно обращаться к одному TCP-соединению в попытках получить текущее состояние девайсов

spoonbob ()