Нормальное завершение потока, из которого запускается асинхронная функция

Поддержка
  • Асинхронная функция запускается из основного потока. Сам скрипт многопоточный, и в каждом потоке БАСа запускается асинхронная функция как-то так:

    Main:
    -- действия 1
    -- async function
    -- действия 2.

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

    Вот настройки:
    Screenshot_2.png
    Можно запускать ее в onApplicationStart, но это не очень подходит.

  • Вы пытаетесь сделать многопоток в многопотоке ... Упростить никак ?
    Задачу опишите и может подскажут ...

  • В скрипте нужно отменять активации номеров. Если отменить номер незадолго после взятия, то сервис СМС вернет ошибку. Поэтому я сделал функцию, которая в течение 2 минут пробует отменить номер, и вызываю ее как асинхронную функцию. Чтобы отмена номера выполнялась как фоновая задача, и основные потоки ее не ждали.

    Ожидаемое поведение: поток баса вызывает функцию отмены номера и, не дожидаясь результата асинхронной функции, запускает следующее выполнение.

    Как работает по факту: поток вызывает функцию асинхронно, не дожидаясь ее завершения, выполняет действия до конца. Но не запускает следующее выполнение, а ждет пока асинхронная функция завершится.

    Как можно сделать:
    Вызывать асинхронную функцию из onApplicationStart, брать таски на отмену из какой-нибудь очереди, вроде базы данных или глобальной переменной. Но тогда будет ряд проблем:

    1. Как понять, когда стопать функцию? Думал получать количество запущенных потоков, и когда их 0 и очередь пустая, останавливать. Но в БАСе вроде получить количество запущенных потоков нельзя.
    2. Создать глобальную переменную, в которой инкременировать на 1, когда поток запущен и уменьшать, когда останавливается. Но в случае непредвиденных ошибок счетчик уменьшаться не будет. Придется весь скрипт пихать в игнор, что затруднит уже дебаг.
    3. Накладные расходы в виде очереди, где надо хранить таски.

    В общем, значительно проще запускать фоновую задачу из обычного потока, и передавать ей что нужно. Но очень странно, что поток ждет асинхронную функцию.

    P.S. Написал тестовый шаблон, но он работает нормально, магия какая-то.
    test_async_function.xml

  • Так не ждите выполнения асинхронной - там по сути просто гет запрос ... даже если в цикле ... По идеи вообще один ...

  • @thepappo said in Нормальное завершение потока, из которого запускается асинхронная функция:

    В скрипте нужно отменять активации номеров. Если отменить номер незадолго после взятия, то сервис СМС вернет ошибку.

    Я написал отдельный скрипт, который по API получает номера, которые вот-вот истекут и отменяет их. Скрипт запускается в цикле каждую минуту.

    Например, номер дается на 20 минут, поток в скрипте у меня не может взять номер на это время, а может, к примеру на 5 минут максимум, значит я могу смело отменять все номера, которые были взяты более 15 минут назад.

    5 минут взяты для примера. Я знаю, что я беру номер из сервиса, что-то там делаю, жду смс-ку и все это занимает у меня максимум эти самые 5 минут.

    Сам скрипт ни о каких потоках, скриптах и так далее понятия не имеет и работает автономно.

    import logging
    import os
    import datetime
    import time
    
    from dotenv import load_dotenv
    from smsactivate.api import SMSActivateAPI
    
    # Configure logging
    logging.basicConfig(level=logging.DEBUG)
    logger = logging.getLogger("[sms_activate]")
    
    # Load environment variables from .env file
    ABS_PATH = os.path.dirname(os.path.abspath(__file__))
    load_dotenv()
    
    
    def main(api_key: str, threshold_seconds: int = 15 * 60):
        """
        Main function to check and process active activations.
    
        Args:
            api_key (str): API key for SMSActivate service.
            threshold_seconds (int): Threshold in seconds to consider activation time.
    
        Returns:
            None
        """
        sa = SMSActivateAPI(api_key)
        sa.debug_mode = True
    
        # Get active activations
        activations = sa.getActiveActivations()
    
        if activations.get("error") == 'NO_ACTIVATIONS':
            logger.info("No pending activations found.")
            return False
    
        # Check if activations retrieval was successful
        if activations.get("status") != 'success':
            raise Exception("Activation status is not success")
    
        # Loop through active activations
        for one in activations["activeActivations"]:
            if one["smsCode"]:
                # Skip if activation is already used
                continue
    
            # Parse activation time
            activation_time_str = one["activationTime"]
            activation_time = datetime.datetime.strptime(activation_time_str, '%Y-%m-%d %H:%M:%S')
            current_time = datetime.datetime.now()
    
            # Calculate time difference in seconds
            time_difference = current_time - activation_time
            seconds_difference = time_difference.total_seconds()
    
            # Log the time difference
            logger.info("Seconds difference from activation time to now: %s" % seconds_difference)
    
            # Check if activation time exceeds threshold
            if seconds_difference > threshold_seconds:
                logger.info("Activation time exceeds threshold for: %s" % one)
                # Mark activation as used
                sa.setStatus(id=one["activationId"], status=8)
    
    
    if __name__ == '__main__':
        # Get SMS Activate API key from environment variables
        sms_activate_api_key = os.getenv("SMS_ACTIVATE_API_KEY")
        if not sms_activate_api_key:
            raise Exception('SMS_ACTIVATE_API_KEY environment variable not set')
    
        sms_activate_api_key = sms_activate_api_key.strip()
    
        time_to_sleep = 60
        while True:
            try:
                main(api_key=sms_activate_api_key)
            except Exception as e:
                # Log any exceptions
                logger.error(e)
    
            # Wait for 60 seconds before checking again
            logger.info(f"Sleeping for {time_to_sleep} seconds")
            time.sleep(time_to_sleep)
    
    
  • @sergerdn может чего не понял, но поток взял номер, 5 минут ждал, смс не пришла - шлем отмену идем дальше ? или там как то сложнее ?

  • @thepappo понял, во все просто оказывается ... Сергедрын взял и все по полочкам разложил ... Я правда его задачи до конца не понял, по какой причине поток не может взять больше чем на пять, и почему если он не дождался не может отбить что не судьба, но главное все по уму, по науке ....

  • @sergerdn так а при чем тут питон?

  • @thepappo В любой непонятной ситуации надо засыпать аргументами :)

  • @Nicolas said in Нормальное завершение потока, из которого запускается асинхронная функция:

    Так не ждите выполнения асинхронной

    В том и прикол, что я не жду. В предыдущем посте шаблон выложил тестовый, он нормально работает. А рабочий - нет ). Очевидно, что-то накосячил я сам.

  • @thepappo Ну в вашем тестовом все правильно ничего не ждете, идете дальше - главное чтоб там предохранитель был, чтоб не подвисло случайно ...

  • @thepappo said in Нормальное завершение потока, из которого запускается асинхронная функция:

    @sergerdn так а при чем тут питон?

    Можно реализовать эту логику на каком угодно языке.

    И получить тоже самое, один автономный скрипт отмены и куча скриптов на BAS, что берут номера из смс-сервиса и не имеют никакой логики отмены их. Я пишу всякие утилиты на python, ты можешь писать на чем угодно.

  • @thepappo said in Нормальное завершение потока, из которого запускается асинхронная функция:

    @Nicolas said in Нормальное завершение потока, из которого запускается асинхронная функция:

    Так не ждите выполнения асинхронной

    В том и прикол, что я не жду. В предыдущем посте шаблон выложил тестовый, он нормально работает. А рабочий - нет ). Очевидно, что-то накосячил я сам.

    в ошибку и на фейл внутри асинхронной ...

  • Короче, там эти асинхронные функции в нескольких местах вызывались. И в одном я забыл обычную функцию поменять на асинхронную. Все ок, в общем, работает )

  • @sergerdn said in Нормальное завершение потока, из которого запускается асинхронная функция:

    один автономный скрипт отмены и куча скриптов на BAS

    Я понимаю, что можно. Я выше и про запуск демона из onApplicationStart писал. И я сам пишу на питоне ). Но в данном случае надо было именно из потока запускать таску.

  • 0 Votes
    6 Posts
    533 Views
  • 0 Votes
    3 Posts
    463 Views
  • 0 Votes
    6 Posts
    941 Views
  • 0 Votes
    11 Posts
    1118 Views
  • 0 Votes
    7 Posts
    837 Views