Ответы пользователя по тегу Асинхронное программирование
  • Почему asyncio.current_task() не передается в функцию?

    Vindicar
    @Vindicar
    RTFM!
    Цитата из документации:
    Return the currently running Task instance, or None if no task is running.

    Как я это понимаю, если ты запланировал выполнение функции как задачи через asyncio.create_task(), то эта функция, и все, вызываемые в ней, смогут получить объект задачи, вызвав asyncio.current_task().
    Если же управление в текущую функции было передано без использования задач на любом из уровней, только через await вызовы, то current_task() вернёт None.

    Но вообще довольно странно. Эксперимент показывает, что хотя бы одна задача должна быть.
    Вот код

    import asyncio
    
    async def print_task():
        print(asyncio.current_task())
    
    
    async def foobar():
        await print_task()
    
    
    async def main():
        print('Direct call')
        await foobar()
        print('create_task')
        task = asyncio.create_task(foobar())
        await task
    
    # asyncio.run(main())  # даст такой же результат
    asyncio.get_event_loop().run_until_complete(main())

    На питоне 3.11 код выводит два объекта задачи, один создаётся run() или run_until_complete(), а другой - create_task(). Тогда получается, что получить None невозможно.
    Ответ написан
    Комментировать
  • Как правильно обработать exception PasswordHashInvalidError?

    Vindicar
    @Vindicar
    RTFM!
    Огранизовать цикл while True, при вводе правильного пароля (т.е. если не было исключения) делать break.
    while True:
        try:
            attempt_stuff_and_things()
        except SomeException as err:
            react_to_error(err)
        else:  # исключения не было
            break
    Ответ написан
    Комментировать
  • Как использовать cfscrape асинхронно?

    Vindicar
    @Vindicar
    RTFM!
    loop.run_in_executor() чтобы запустить код в отдельном потоке, и обернуть этот поток в асинхронный таск.
    Ответ написан
  • Почему асинхронный телебот увеличивает кол-во потоков?

    Vindicar
    @Vindicar
    RTFM!
    while True:
                print(1)
                await asyncio.sleep(1)

    Корутина может выполняться в нескольких экземплярах параллельно. У тебя в коде вечный цикл, поэтому старые запущенные экземпляры никогда не завершаются, так что при запуске новых у тебя появляется всё больше и больше экземпляров.
    Ответ написан
  • Как запустить две функции параллельно в asyncio?

    Vindicar
    @Vindicar
    RTFM!
    Намудрил-то, намудрил-то...
    background_func() сделай асинхронной, run_in_executor() в ней нафиг не нужен, можно будет просто сделать await bot.send_message(...).
    time.sleep(), соответственно, меняешь на await asyncio.sleep().
    executor.start_polling() вызывай прямо в теле программы, без выкрутасов.

    Для запуска функции при старте бота опиши её как
    async def bot_startup(dp: Dispatcher):
    и передай её в параметре on_startup при вызове start_polling().
    А внутри bot_startup уже делай что нужно, например, asyncio.create_task(background_func()).
    Обрати внимание, без await - тогда background_func() будет работать параллельно боту.
    Ответ написан
    2 комментария
  • Как вернуть значения из синхронного колбека в corotine несколько раз?

    Vindicar
    @Vindicar
    RTFM!
    Я вижу два потенциальных источника проблем.
    1. Ты говоришь, что библиотека будет дёргать callback при изменении значения свойства. А когда она будет это делать?
    Ей ведь нужно выполнить для этого какой-то код, причём наверняка синхронный. Пусть какой-то метод, который проверит очередь событий, извлечёт оттуда новые и дёрнет соответствующие callbak'и. Этот код точно вызывается?
    2. Ты делаешь await iface.call_method('bla'). Ты уверен, что обновление свойства произойдёт после завершения await? Если оно произойдёт до, то твой вызов wait_property() не увидит обновлений. Возможно, стоит вешать callback до вызова call_method()?
    Ответ написан
    Комментировать
  • Почему код не работает асинхронно?

    Vindicar
    @Vindicar
    RTFM!
    async def parse_card(card):
        d = dict()
        # из карточки берется html
        # "вставляется" в объект супа
        # и возвращается словарь
        # внутри этой функции не используются await
        return d


    Ну а ты что хочешь-то? Асинхронность в питоне предназначена для распараллеливания операций ввода-вывода (и сводимых к ним), а не для параллельного выполнения расчётов. У тебя parse_card() всё равно что синхронная.
    Ответ написан
    1 комментарий
  • Как запустить выполнение асинхронной функции с определенной частотой выполнения?

    Vindicar
    @Vindicar
    RTFM!
    Храни в asyncio.Queue очередь запросов к API. Отдельная задача пусть выбирает запросы из очереди, отправляет, получает ответ и оповещает о результате. Например, так.
    import asyncio
    import typing
    
    class ThrottledResource:
        def __init__(self, delay: float):
            self._delay = delay
            self._queue = asyncio.Queue()
            self._task = None
        
        def start(self):
            self._task = asyncio.create_task(self._work_loop)
        
        def stop(self):
            self._task.cancel()
            self._task = None
    
        # этот метод вызывается клиентским кодом, получает параметры и возвращает отклик спустя время.
        async def query(self, params):
            future = asyncio.Future()  # Future просигналит, когда наш запрос будет обслужен
            await self._queue.put((future, params))
            result = await future  # корутина спит, пока запрос не обслужат
            return result
    
        async def _work_loop(self):
            while True:
                future, params = await. self._queue.get()  # ждем, пока не придёт запрос
                try:
                    result = await call_api(params)  # тут делаем асинхронное обращение к сервису
                except Exception as err:
                    future.set_exception(err)  # была ошибка - теперь await future выкинет исключение
                else:
                    future.set_result(result)  # полуен результат - await future вернёт его
                self._queue.task_done()  # каждому успешному get() соответствует task_done()
                asyncio.sleep(self._delay)  # можно учесть, сколько времени делался запрос. Но стоит ли?

    Код примерный, но идею передаёт. Использоваться будет как-то так
    api = ThrottledResource(delay=1.0)
    api.start()
    ...
    result = await api.query(params)  # await подождёт, пока не дойдёт очередь до нашего запроса


    Нужно добавить обработку ошибок, корректное завершение работы при наличии задач в очереди, и так далее.
    Технически вместо класса можно было реализовать это всё в виде декоратора над replier(), но это уже на вкус и цвет.
    Ответ написан
    6 комментариев
  • Насколько критично использование pydantic в асинхронных приложениях?

    Vindicar
    @Vindicar
    RTFM!
    Не стоит спользовать длительные синхронные вызовы в асинхронном коде.
    Во-первых, валидация - это CPU-bound задача, а не IO-bound. Её можно вынести в поток, но и только. Просто асинхронно её не выполнишь.
    Во-вторых, если у тебя не ChatGPT данные валидирует, скорее всего время выполнения валидации будет пренебрежимо малым.
    Ответ написан
    Комментировать
  • Можно ли узнать какая корутина вызвала ошибку в asyncio.gather?

    Vindicar
    @Vindicar
    RTFM!
    Вообще-то gather() возвращает исключения и результаты в том же порядке, в котором переданы корутины. Всегда.
    Но ты сам себе выстрелил в ногу вот этим: *{f1(), f2()}
    Ты указал литерал множества, а это не упорядоченная коллекция. Так что в каком порядке были переданы корутины - хз.
    Положи их в кортеж или в список перед отдачей в gather(), и проблема уйдёт - элемент выходного списка с индексом i будет соответствовать корутине с индексом i в исходном кортеже/списке.
    Ответ написан
    Комментировать
  • Фундаментальное отличие async await в python и javascript?

    Vindicar
    @Vindicar
    RTFM!
    Разницы нет, в том числе в твоём описании.
    Текущая корутина при await-вызове приостанавливается, её состояние сохраняется.
    При этом реактор может заниматься другими вещами (в частности, выполнением await-вызова).
    Когда await-вызов завершиться, состояние корутины будет восстановлено, и она продолжит выполнение.
    Ответ написан
    4 комментария
  • Как получить результат выполнения задачи в Asyncio?

    Vindicar
    @Vindicar
    RTFM!
    await tay
    Угадай с трёх раз, что возвращает это выражение?
    Правильно. Значение, которое вернёт корутина, запущенная в рамках задачи. Просто присвой его переменной.
    А вообще в твоём случае и задачи-то не требуются.
    Освой asyncio.gather().
    Она позволит выполнить переданные корутины одновременно, и вернёт массив их результатов.
    results = await asyncio.gather(coro1(x,y), coro2(a,b), ...)
    res1, res2, ... = results
    Ответ написан
    Комментировать
  • Не будет ли этот код блокировать асинхронный код?

    Vindicar
    @Vindicar
    RTFM!
    Проблема в том, что в Питоне потоки немножко увечные (ключевое слово GIL), а потому реально работают только в двух случаях:
    1. Исполняемый длительный код написан не на питоне, а принадлежит одному из бинарных модулей
    2. Исполняемый код большую часть времени ждёт (например, операции ввода/вывода).
    В случае с многопроцессностью проблем меньше, но появляетя проблема обмена данными между процессами.

    Я бы запустил несколько процессов воркеров, которые умеют рендерить предоставленные данные и возвращать график как массив байт, содержащий изображение. Тогда основной бот кидает задание в некую очередь, один из воркеров просыпается, рендерит задание, и кладёт ответ в другую очередь. Словом, паттерн поставщик-потребитель. Конечно, могут быть проблемы с тем, чтобы подружить multiprocessing.Queue с асинхронным кодом, но имхо так всё равно изящнее.
    Более того, если искомые данные лежат в БД или ином внешнем источнике - можно нагрузить воркеров их извлечением. Тогда задание будет содержать в себе только критерии выборки данных.
    Ответ написан
  • Как сделать асинхронное выполнение функций в Python3?

    Vindicar
    @Vindicar
    RTFM!
    inpit() асинхронным сделать можно как показано выше.

    А дальше
    async def sender():
        while True:
            ...
    
    async def getter():
        ...
    
    async def main():
        asyncio.create_task(sender())
        await getter()
    
    asyncio.run(main())
    Ответ написан
    Комментировать
  • Пишу бота для телеграм (telebot), проблема с асинхронными функциями. В чем причина ошибки?

    Vindicar
    @Vindicar
    RTFM!
    1. await так не пишется.
    x = await asyncio.create_task(startq(message))
    2. Зачем ты делаешь await?
    asyncio.create_task() создаёт корутину, которая будет выполняться конкуррентно с текущей (как бы параллельно)
    await приостанавливает выполнение текущей корутины, пока не завершится вызываемая (справа от await).
    Т.е. await create_task(...) - это не очень осмысленно: сначала запускаешь параллельную корутину, а потом всё равно стоишь и ждёшь её.
    Если тебе нужно запустить корутину startq() и дождаться её выполнения (ну и получить возвращаемое значение) - пиши просто x = await startq(message)
    Если тебе нужно запустить корутину startq() и пусть она дальше выполняется сама по себе, тебе от неё ничего не надо - достаточно будет asyncio.create_task(startq(message))
    Ответ написан
    1 комментарий
  • Как запустить асинхронную задачу параллельно синхронным?

    Vindicar
    @Vindicar
    RTFM!
    Альтернативно, можно задействовать попробовать переписать код на асинхронный, а строго синхронные задачи запускать в отдельном потоке через run_in_executor().
    Ответ написан
    Комментировать
  • Как запустить фоновую задачу?

    Vindicar
    @Vindicar
    RTFM!
    Когда ты делаешь await, ты говоришь "останови работу данной корутины, дождись, пока не закончится операция после await, потом продолжи работу". Пока одна корутина остановлена, другие могут выполняться. В общем, почитай про то, как работает асинхронная программа.
    Если тебе нужно запустить корутину, не дожидаясь её завершения, asyncio.create_task() в помощь.

    Стоит понимать, что код, который ты запускаешь через asyncio.create_task(), должен тоже быть асинхронный! Если ты завернёшь обычный синхронный вызов в корутину, и запустишь её через create_task(), чуда не будет - бот всё равно встанет колом, пока вызов не закончится.
    Так что взгляни лучше на свой парсер - если он основан на синхронной библиотеке типа requests, перепиши его на aiohttp.
    Ответ написан
  • Как сделать независимый while в python?

    Vindicar
    @Vindicar
    RTFM!
    1. Не надо так делать. Сайту твоя долбёжка запросами не понравится. Как минимум, введи задержку от 10 минут и более.
    2. Ты ознакомился с тем, как вообще работают асинхронные приложения? Судя по терминологии вроде "независимый while", нет. Почитай на эту тему, она не тривиальная, но без понимания основ асинхронщины ты далеко не уедешь.
    3. Зависит от библиотеки, но так или иначе тебе нужна корутина, которая сначала делает запрос к сайту (лучше тоже асинхронно, с помощью aiohttp) и обновляет хранимые в кэше сведения, а потом спит нужный интервал времени (и не с помощью time.sleep()!). Эту корутину надо запустить в рабочем цикле (loop) asyncio, а потом уже запускать остального бота. Тогда боту практически всегда будет доступен этот кэш.
    Ответ написан
    Комментировать