Как выполнять бесконечный цикл, чтобы он не блокировал работу всей программы?
Пишу телеграмм бота, который парсит данные с сайта (объявления). Сайт довольно часто обновляется, поэтому необходимо постоянно делать запросы на сайт: «не прилетело ли новое объявление»?
Собственно, поэтому было принято решение обернуть эти запросы в бесконечный цикл. Написав пару декораторов и один бесконечный цикл для обработки сообщений, я понял, (естественно) при этом скрипт само собой не может выполнить декораторы обработки входящих сообщений, тк висит в цикле.
Скрипт пишу на python с асинхронной библиотекой aiogram (изначально думал что она мне как-то поможет).
Вопрос состоит в следующем: как можно распараллелить данные процессы, чтобы и обновления отслеживать, и при этом обрабатывать входящие сообщения
ЗЫ: бота переписываю уже не первый раз, пытался найти ответ сам, хотел сюда прикрутить потоки или процессы, но не могу понять что в моем случае оптимальнее раз на то пошло, и как это правильно обыграть?
цикл в коде просто для отладки, ровно как и print в этом цикле
import config import aiogram from aiogram import Bot, Dispatcher, executor, types API_TOKEN = config.token # Инициализация бота и диспетчера bot = Bot(token=API_TOKEN) dp = Dispatcher(bot) #------------------------Цикл --------------------------------------------------------------- #Будущий парсер while 1: print('цикл') #------------------------Блок обработки входящих сообщений------------------------------ @dp.message_handler(commands=['start']) #Приветствие async def echo(message: types.Message): await message.answer('''Здравствуйте Вы подключились к боту . Чтобы узнать возможности Бота введите команду /help''') @dp.message_handler(commands=['help']) #Помощь async def echo(message: types.Message): await message.answer('help') @dp.message_handler(commands=['add']) #Функция добавления фильтра async def echo(message: types.Message): await message.answer('add') if __name__ == '__main__': executor.start_polling(dp, skip_updates=True)
Средний 2 комментария
Asyncio, ждут и бесконечные циклы
У меня есть битва, которая выполняет задачу каждые 2 секунды. Я попытался использовать для этого бесконечный цикл, но сценарий аварийно завершился, и Task was destroyed but it is still pending! Я читал о asyncio сопрограмм, но ни один из примеров, которые я нашел применение await в них. Можно ли избежать этой ошибки, например, запустив сопрограмму с await ?
Task was destroyed but it is still pending! это предупреждение, которое вы получаете при вызове loop.close() когда некоторые задачи в вашем скрипте не завершены. Обычно вам следует избегать этой ситуации, поскольку незавершенная задача может не выделять некоторые ресурсы. Вам нужно либо ждать выполнения задачи, либо отменить ее до закрытия цикла события.
Поскольку у вас бесконечный цикл, вам, вероятно, потребуется отменить задачу, например:
import asyncio from contextlib import suppress async def start(): # your infinite loop here, for example: while True: print('echo') await asyncio.sleep(1) async def main(): task = asyncio.Task(start()) # let script some thime to work: await asyncio.sleep(3) # cancel task to avoid warning: task.cancel() with suppress(asyncio.CancelledError): await task # await for task cancellation loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) try: loop.run_until_complete(main()) finally: loop.run_until_complete(loop.shutdown_asyncgens()) loop.close()
См. Также этот ответ для получения дополнительной информации о задачах.
Блог
Запустите бесконечный цикл в фоновом режиме и получите доступ к переменной класса из цикла с помощью Python asyncio
#python #loops #python-asyncio
#python #циклы #python-asyncio
Вопрос:
У меня есть класс, Sensor который имеет одно числовое значение num . У него есть async метод update , который обновляет num каждую секунду в бесконечном цикле.
Я хочу инициировать класс и вызвать метод update , продолжить его выполнение и вернуть элемент управления основной программе для доступа к значению num через некоторое время. Я использую asyncio , но не знаю, как вызвать метод таким образом, чтобы одновременный запуск цикла и доступ к переменной были возможны.
import asyncio import time class Sensor: def __init__(self, start=0): self.num = start async def update(self): print(f"Starting Updates ") while True: self.num = 1 print(self.num) await asyncio.sleep(1) if __name__ == "__main__": print("Main") sensor = Sensor() # asyncio.run(sensor.update()) # asyncio.ensure_future(sensor.update()) future = asyncio.run_coroutine_threadsafe(sensor.update(), asyncio.new_event_loop()) print("We are back") print(f"current value: ") time.sleep(4) print(f"current value: ")
Это дает мне вывод 0 до и после ожидания в течение 4 секунд, что означает, что update метод не выполняется с задержкой. run() вообще не возвращает элемент управления.
Какой метод я должен вызвать, чтобы вызвать бесконечный цикл в фоновом режиме?
Ответ №1:
Чтобы использовать asyncio.run_coroutine_threadsafe , вам нужно фактически запустить цикл событий в отдельном потоке. Например:
sensor = Sensor() loop = asyncio.new_event_loop() threading.Thread(target=loop.run_forever).start() future = asyncio.run_coroutine_threadsafe(sensor.update(), loop) . loop.call_soon_threadsafe(loop.stop)
Комментарии:
1. В нем говорится run is not defined . Я сделал import threading . уверен, что здесь отсутствует что-то основное.
2. @harvpan Это была опечатка. Теперь я отредактировал ответ.
3. Работает отлично. Спасибо.
Ответ №2:
Согласно документам, для одновременного выполнения задач вам следует использовать gather . Например, ваш код будет выглядеть примерно так:
import asyncio import time class Sensor: def __init__(self, start=0): self.num = start async def update(self): print(f"Starting Updates ") while True: self.num = 1 print(self.num) await asyncio.sleep(1) async def print_value(sensor): print("We are back") print(f"current value: ") await asyncio.sleep(4) print(f"current value: ") async def main(): print("Main") sensor = Sensor() await asyncio.gather(sensor.update(), print_value(sensor)) if __name__ == "__main__": asyncio.run(main())