Как организовать многопоточность с 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 шт):

Автор решения: Roman-Stop RU aggression in UA

Чтоб запустить корутину (асинхронную функцию) паралельно нужно использовать asyncio.create_task.

Так как в вашем случае у вас функция для запуска не асинхронная, то нужно ее сначала превратить в асинхронную. Тут есть два принципиальных варианта:

  1. сделать саму функцию (в вашем случае отсыки смс) асинхронной, т.е. переписать ее используя внутри только асинхронные вызовы к другим функциям типа запросов по сети и т.д.
  2. конвертировать синхронную функцию в асинхронную с помощью 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())

→ Ссылка