Как запустить несколько функций одновременно в pyTelegramApi?
Использую библиотеку pyTelegramApi и нужно запустить несколько функций одновременно прямо в телеграм боте. Я нажимаю кнопку и функции запускаются одновременно. У меня сейчас вот так:
if message.text == 'Включить':
while True:
f1 = function1(chat_id)
f2 = function2(chat_id)
f3 = function3(chat_id)
bot.send_message(chat_id, text=f'{f1}\n{f2}\n{f3}')
Как видите, функции запускаються последовательно а не паралельно, как это можно сделать?
UPD:
Дополняю, так как не совсем понятно. Бесконечный цикл работает, так как мне нужно чтобы каждое N времени эти функции выполнялись, там идет запрос по api и парсинг информации. Я хочу чтобы эти функции выполнялись не последовательно (так как это занимает длительное время), а паралельно! По итогу каждая функция return значение, которое отправляет бот. Как можно это более менее суразно сделать?
Ответы (3 шт):
В стандартной библиотеке python есть concurrent.futures.ThreadPoolExecutor, который является обёрткой над модулем threading. Он позволяет удобно запускать несколько потоков и получать результаты выполнения функций.
from concurrent.futures import ThreadPoolExecutor
from time import time, sleep
def function1(*args):
sleep(2) # Условно ждём ответ от сервера.
return f"Первый ответ: {args}"
def function2(*args):
sleep(5) # Условно долгие вычисления
return f"Самый долгий ответ: {args}"
def function3(*args):
return f"Моментальный ответ: {args}"
with ThreadPoolExecutor() as executor:
while True:
t = time()
futures = []
print("Запускаем потоки...")
for func in [function1, function2, function3]:
future = executor.submit(func, 42)
futures.append(future)
answers = []
print("Ждём результаты...")
for future in futures:
result = future.result()
answers.append(result)
print(f"Готово! Прошло {time()-t:.2f} секунд", *answers, sep="\n")
В консоль мы получаем:
Запускаем потоки...
Ждём результаты...
Готово! Прошло 5.01 секунд
Первый ответ: (42,)
Самый долгий ответ: (42,)
Моментальный ответ: (42,)
Запускаем потоки...
...
Как видим, программы выполнились параллельно в нескольких потоках.
так как мне нужно чтобы каждое N времени эти функции выполнялись
Возможно, вам нужна библиотека sheduler или встроенный sched
В целом вы можете посмотреть в сторону асинхронных библиотек: aiohttp для запросов и aiogram для создании бота. Это не обязательно, но почему бы и нет.
Для запуска нескольких функций одновременно в pyTelegramApi вы можете использовать многопоточность. Один из способов - это использование модуля threading.
Например, вы можете создать три потока для выполнения каждой функции и ожидать их завершения с помощью метода join(). Вот пример кода:
import threading
if message.text == 'Включить':
f1_result = None
f2_result = None
f3_result = None
def run_f1():
global f1_result
f1_result = function1(chat_id)
def run_f2():
global f2_result
f2_result = function2(chat_id)
def run_f3():
global f3_result
f3_result = function3(chat_id)
# создаем три потока для выполнения каждой функции
t1 = threading.Thread(target=run_f1)
t2 = threading.Thread(target=run_f2)
t3 = threading.Thread(target=run_f3)
# запускаем каждый поток
t1.start()
t2.start()
t3.start()
# ожидаем завершения всех потоков
t1.join()
t2.join()
t3.join()
# отправляем результаты каждой функции
bot.send_message(chat_id, text=f'{f1_result}\n{f2_result}\n{f3_result}')
Код создает три потока, каждый из которых выполняет одну из функций. После запуска всех потоков они ожидаются с помощью метода join(). Затем результаты каждой функции отправляются в чат с помощью метода bot.send_message(). Обратите внимание, что результаты каждой функции сохраняются в переменных f1_result, f2_result и f3_result, объявленных вне функций, чтобы они были доступны после завершения потоков.
Достаточно воспользоваться async / await:
import asyncio
async def function1(*args):
await asyncio.sleep(1)
return f"function1 done: {args}"
async def function2(*args):
await asyncio.sleep(3)
return f"function2 done: {args}"
async def function3(*args):
return f"function3 done: {args}"
async def main():
return await asyncio.gather(function1(1), function2(2), function3(3))
if __name__ == '__main__':
import time
print("Запускаем потоки...")
t = time.perf_counter()
res = asyncio.run(main())
elapsed = time.perf_counter() - t
print(f"Выполнились за {elapsed:0.2f} сек.", *res, sep="\n" )
Результат выполнения:
Запускаем потоки...
Выполнились за 3.00 сек.
function1 done: (1,)
function2 done: (2,)
function3 done: (3,)
Если приблизить к Вашей задаче, то:
...
async def start_func_wait_answer():
return await asyncio.gather(function1(1), function2(2), function3(3))
return group
def main():
#f1 = function1(chat_id)
#f2 = function2(chat_id)
#f3 = function3(chat_id)
res = asyncio.run(start_func_wait_answer())
#bot.send_message(chat_id, text=f'{f1}\n{f2}\n{f3}')
print(*res, sep="\n" )
if __name__ == '__main__':
import time
print("Запускаем потоки...")
t = time.perf_counter()
# if message.text == 'Включить':
main()
elapsed = time.perf_counter() - t
print(f"Выполнились за {elapsed:0.2f} сек.")