LINUX.ORG.RU

Смешанный асинхронный и синхронный код

 ,


0

1

Привет, ребятки. У меня в одном проекте код смешанный, есть асинхронщина и обычный код.
В обычном коде присутствует множество time.sleep(n). В асинхрощине есть запуск функции loop.add_reader(...), но sleep'ы приостанавливают работу. Если заменить вызовы time.sleep на asyncio.run(asyncio.sleep(n)) это решит проблему и насколько это криво?
От sleep'ов отказать не могу, переписать все на asyncio тоже не хочется) На данный момент add_reader заменена на обычный цикл, что мне не нравится...



Последнее исправление: cadogan (всего исправлений: 4)

переписать все на asyncio тоже не хочется

Придётся.

У меня в одном проекте код смешанный, есть асинхронщина и обычный код

Не бывает смешанного кода, бывает асинхронный код, в котором блокирующие операции заворачивают в экзекуторы. Это или to_thread или run_in_executor.

vvn_black ★★★★★
()
Ответ на: комментарий от upcFrost

Sleep работает только по текущему треду.

А что, GIL уже отменили?

UPD: ага, прочитал доку, был неправ

Zeta_Gundam
()
Последнее исправление: Zeta_Gundam (всего исправлений: 1)
Ответ на: комментарий от cadogan

Просто sleep не криво, там сервер за частые запросы банит по ip, sleep’ы нужны для обхода банов)

Не нужны здесь слипы, совсем не нужны. Меняйте архитектуру приложения или запускайте каждый запрос по планировщику синхронным кодом.

vvn_black ★★★★★
()
Последнее исправление: vvn_black (всего исправлений: 2)
Ответ на: комментарий от cadogan

Слипом наказывать запрос? 😄

Назло врагу уши морозите ))

PS: за слип в коде по рукам надо бить. Сильно бить. (У самого в одном проекте в тестах имеется, но я прям с отвращением писал ибо ограничен был по ресурсам и знал, что это времянка)

ergo ★★★
()
Последнее исправление: ergo (всего исправлений: 1)
Ответ на: комментарий от ergo

Ещё раз повторю. Бесконечный цикл запросов на сервер, в конце цикла добавить sleep 5 секунд никаких проблем не вижу. По рукам себя стукни ;-) Ну раз слип блокирует асинхронный код в треде другом, то придётся переделать, да.

cadogan
() автор топика
Последнее исправление: cadogan (всего исправлений: 1)
Ответ на: комментарий от cadogan

Бесконечный цикл запросов на сервер, в конце цикла добавить sleep 5 секунд никаких проблем не вижу.

Ещё раз, запускай бесконечный цикл запросов с блокирующим io в отдельном потоке, оформляя через экзекутор или to_thread.

В документации всё есть - https://docs.python.org/3/library/asyncio-task.html#running-in-threads

vvn_black ★★★★★
()
Ответ на: комментарий от Zeta_Gundam

Как раз через sleep можно частично контролировать gil и смену контекста. Аналогично через asyncio.sleep можно пропустить цикл без реального ожидания. Полезно когда есть нужно собрать результат пачки тасков но при этом нет возможности напрямую использовать скажем gather (например таски прикрыты толстым слоем 3rd party кода).

Делал такое для graphql для снятия проблемы N+1 при вложенных подзапросах - все запросы собираются путем пропуска одного цикла когда реальный запрос по сети уже отработал и остались только вызовы локальных обработчиков которые гарантированно завершатся в пределах цикла, а других вариантов связать запросы в кошках написанного на С фреймворка просто нету. Получается довольно короткое и красивое, хоть и несколько упоротое решение

upcFrost ★★★★★
()
Последнее исправление: upcFrost (всего исправлений: 1)

Ну, тут если ты начал писать асинхронный код, то и все абстракции асинхронщины придётся использовать. Те весь код должен крутиться в асинк цикле самого движка, через вызов и создание абстракций. А ты спрашиваешь как не использовать абстракции и при этом взаимодействовать с кодом, который уже работает внутри этих абстракций. Это как через ж гланды выдирать. Всё переписывай или на свой асинк ио или лучше родной питонячий await async используй, он вполне тру для несложных вещей

menangen ★★★★★
()
Ответ на: комментарий от menangen

Ты говоришь очевидные вещи, это итак понятно. Я просто хотел использовать в одном треде асинхронный евент луп :-) Вот такой код работает без проблем со слипами в основном треде.

    async def _run(self) -> None:
        self._pconnection.listen('notify_listing')
        self._exit = 1
        while True:
            if self._exit == 2:
                break
            self._handle_notify()
            await asyncio.sleep(1)

    def __call__(self, *args, **kwargs) -> None:
        loop: any = asyncio.new_event_loop()
        # loop.add_reader(self._pconnection.connection, self._handle_notify)
        loop.run_until_complete(self._run())
А вот если вместо бесконечного цикла заюзать add_reader, то уже не работает) Вообще все это затевалось чтобы заюзать add_reader, вместо цикла...

cadogan
() автор топика
Последнее исправление: cadogan (всего исправлений: 1)
Ответ на: комментарий от upcFrost

sleep в асинхронщине вообще не нужен так-то :-) sleep юзается в основном синхронном треде в бесконечном цикле.
Вон кусок сверх кинул.

cadogan
() автор топика
Последнее исправление: cadogan (всего исправлений: 1)

заменить вызовы time.sleep на asyncio.run(asyncio.sleep(n)) это решит проблему и насколько это криво?

Нет. asyncio.run(asyncio.sleep(n)) создаст и запустит event-loop запихнув в него короутину asyncio.sleep(n). Когда короутина завершиться - тогда и завершиться event-loop. Как синхронный код стоял на этом месте, так он и будет стоять, только более высокотехнологическим способом)

От sleep’ов отказать не могу, переписать все на asyncio тоже не хочется)

А придется переписать на asyncio. Потому что асинхронную функцию (желанный тобой sleep) можно запускать и асинхронно дожидаться результата только из асинхронной функции.

dvetutnev
()

Я не настоящий погромист, но я бы на задачи, которые чего-то ждут с помощью sleep, наплодил бы threads, и асинхронно ждал бы результатов. Т.е. переписал бы. А как ты написал - порнография в самом плохом смысле.

Shadow ★★★★★
()

А если (не понимаю нахрена только) тебе так хочется вызывать не измазанный async/await код из асинхронного - используй to_thread, как праивльно уже сказали. И там слипы ничего не блокируют

no-dashi-v2 ★★
()
Ответ на: комментарий от Shadow

Твоя реакция мне непонятна. Я исходил из посылки (да, оказавшейся ложной), что по time.sleep() тред спит, не отпуская GIL.

GIL например на древнем отднопоиочном процессоре не имеет значения вообще, и вообще, это самая меньшая из проблем питона.

Пошли обрадуем всех пользователей модуля multiprocessing, чего люди мучаются.

Zeta_Gundam
()
Ответ на: комментарий от cadogan

Ты сам написал что sleep у тебя по факту блокирует асинхронный код (должен блокировать) по окончанию цикла. Попробуй вместо того чтоб пытаться связать два треда (синхронный и тот где loop бегает) через sleep связать их через ожидание евента. Суть - ты все так же делаешь sleep в синхронный треде, а в асинхронном меняешь sleep на wait. Далее из синхронного треда по выходу из sleep делаешь event.set, запуская новый цикл

upcFrost ★★★★★
()
Ответ на: комментарий от Shadow

GIL например на древнем отднопоиочном процессоре не имеет значения вообще

Таки имеет, ты смешиваешь физические потоки, логические потоки ОС и потоки питона, это три разных вещи с разной логикой переключения

upcFrost ★★★★★
()