Как организовать многопоточность с async функцией в python?
Используется python 10. Нужно из асинхронного метода отправить смс и на время поиска данных не блокировать другие команды пользователя в боте.
class ClassHandlers:
def Check_data(self):
MatchController = ClassMatchController()
ListMatchName = MatchController.GetMatch()
MatchController.PushDB(ListMatchName)
EventController = ClassEventController()
EventController.GetEvents()
list_message = self.get_newmessage()
if list_message != []:
for mes in list_message:
self.send_message_all_was_saved_users(mes)
async def send_message_all_was_saved_users(self, aMessage):
List_id_user = self.get_id_all_users() #это синхронная функция
#стоит ли её вызывать через await self.get_id_all_users() если она может
#занять долгое время на процесс ?
for id_user in List_id_user:
await types.Message.bot.send_message(id_user, aMessage) #здесь срабатывает ошибка,
#которую нужно решить "Check_data never don't await"
async def start_check(self, message: types.Message):#вызывается по команде через бота эта функция,
#что бы повторялись действия выше
if message.from_user.id == '#тут айди пользователя':
check = ClassRepeatSelf(1, self.dsd) #как сюда передать объект message,
# что бы где выше await types.Message.bot.send_message(id_user, aMessage),
#не писать types.Message.
check.start()
Файл ClassRepeatSelf. Здесь реализация под синхронную функцию, потому что асинхронность мешать с синхронностью это ... но пока что другого решения не нашел, что бы можно было повторять действия отправлять из них сообщение и не блокировать другие команды в боте.
from threading import Timer, Thread, Event
from datetime import datetime
import time
class ClassRepeatSelf():
"""A Timer class that does not stop, unless you want it to."""
def __init__(self, seconds, target):
self._should_continue = False
self.is_running = False
self.seconds = seconds
self.target = target
self.thread = None
def _handle_target(self):
self.is_running = True
self.target()
self.is_running = False
self._start_timer()
def _start_timer(self):
if self._should_continue: # Code could have been running when cancel was called.
self.thread = Timer(self.seconds, self._handle_target)
self.thread.start()
def start(self):
if not self._should_continue and not self.is_running:
self._should_continue = True
self._start_timer()
else:
print("Timer already started or running, please wait if you're restarting.")
def cancel(self):
if self.thread is not None:
self._should_continue = False # Just in case thread is running and cancel fails.
self.thread.cancel()
else:
print("Timer never started or failed to initialize.")
Ответы (1 шт):
Чтоб запустить корутину (асинхронную функцию) паралельно нужно использовать asyncio.create_task.
Так как в вашем случае у вас функция для запуска не асинхронная, то нужно ее сначала превратить в асинхронную. Тут есть два принципиальных варианта:
- сделать саму функцию (в вашем случае отсыки смс) асинхронной, т.е. переписать ее используя внутри только асинхронные вызовы к другим функциям типа запросов по сети и т.д.
- конвертировать синхронную функцию в асинхронную с помощью
asynio.to_thread
Первый способ, думаю, понятен.
Что касается второго, то для этого async создает отдельный поток и в нем запускает такую функцию.
Корутину созданную с помощью to_thread можно запускать обычными способами, т.е. делать на ней await в другой корутине (если нужно дождаться окончания) или использовать create_task (если нужно выполнение делать параллельно).
Вот пример с create_task, который для вашего случая подойдет:
import asyncio
import time
def long_running():
for i in range(10):
time.sleep(0.5)
print(f"long: {i}")
print(f"long: finished")
async def main():
asyncio.create_task(asyncio.to_thread(long_running))
for i in range(10):
await asyncio.sleep(1)
print(f"main: {i}")
print(f"finished at {time.strftime('%X')}")
asyncio.run(main())