Нужно ли (и как) позаботиться о завершении работы нескольких потоков, которые тоже запущены из отдельного потока?

Ссылка скопирована
1 ответ

В потоках разбираюсь слабо, поэтому прошу совета.
1. При старте бота запускается дополнительный сервисный поток, который предназначен для периодического (1 раз в минуту) запуска неких функций.
2. Эти функции тоже запускаются каждая в своем потоке, а после окончания работы их потоки должны завершиться.
3. При коротком тестировании все вроде бы работает корректно, никакие конфликты не возникают.

Вопрос: действительно ли потоки корректно завершаются и полностью освобождают занятые ресурсы без постороннего вмешательства, или нужно производить дополнительные действия, например, использовать JOIN? В какой момент его использовать, если я не знаю, когда завершаются потоки, а нужно продолжать работу программы? Нужно ли делать так, как в закомментированном куске кода ниже или оставить так, как сделано в другом куске (без использования JOIN)?
Код см. под спойлером, там же уточняющие вопросы и комментарии.
Отрывок рабочего кода

def run_threaded(func):     func_name = 'af_service: ' + func.__name__     def wrapper(*args, **kwargs):         for item in threading.enumerate():             if item.name == func_name:                 return None         MyThread = threading.Thread(target = func, name = func_name, args = args, kwargs = kwargs, daemon = True)         MyThread.start()         return MyThread     return wrapper   @run_threaded def clean_bot_messages(): # Пример функции, запускаемой в отдельном потоке. Декоратор отрабатывает нормально, поток запускается правильно     if not const.get_garbage_cleaning_state():         return     # дальше тело функции, отрабатывает корректно, проблем нет   def thread_jobs(notify: bool):     while const.get_thread_state():         if notify:             print(cur_time(), '- Работает служебный поток...')             print('Число активных потоков:', threading.active_count())         start_time = time.time()                  # Закомментированный блок выполняет те же действия, что и блок ниже его:         # нужно ли использовать JOIN или потоки самостоятельно завершатся и освободят ресурсы?                  # Вариант 1         #------Работа служебных функций. Они запускаются в отдельных потоках---------         # threads = []         # threads.append(check_schedule())         # threads.append(clean_bot_messages()) # Пример функции, запускаемой в отдельном потоке         # ........ другие функции                  # for item in threads:         #     item.join()         #------Конец работы служебных функций---------                  # Вариант 2         #------Работа служебных функций. Они тоже запускаются в отдельных потоках---------         check_schedule()         clean_bot_messages() # Пример функции, запускаемой в отдельном потоке         # ........ другие функции         #------Конец работы служебных функций---------         time_delta = time.time() - start_time # время, затраченное на работу функций         if notify:             print(cur_time(), f'Служебные функции отработали за {time_delta} сек.')         time.sleep(thread_sleep_time - time_delta)     if notify:         print(cur_time(), '- Служебный поток завершен...')   def start_thread(): # Этот поток запускается при старте бота и работает постоянно, проблем с ним нет     if const.service_thread_is_active():         print('Сервисный поток уже работает...')     else:         print('Сервисный поток еще не активен...')     if const.get_thread_state() and not const.service_thread_is_active():         MyThread = threading.Thread(target = thread_jobs, name='AF_Bot_Service', args = (False,), daemon = True)         MyThread.start()         print('Сервисный поток запущен:', MyThread.name, ', ID:', MyThread.native_id)  if __name__ == '__main__':     start_thread() bot.infinity_polling(skip_pending = True)

def run_threaded(func): func_name = 'af_service: ' + func.__name__ def wrapper(*args, **kwargs): for item in threading.enumerate(): if item.name == func_name: return None MyThread = threading.Thread(target = func, name = func_name, args = args, kwargs = kwargs, daemon = True) MyThread.start() return MyThread return wrapper @run_threaded def clean_bot_messages(): # Пример функции, запускаемой в отдельном потоке. Декоратор отрабатывает нормально, поток запускается правильно if not const.get_garbage_cleaning_state(): return # дальше тело функции, отрабатывает корректно, проблем нет def thread_jobs(notify: bool): while const.get_thread_state(): if notify: print(cur_time(), '- Работает служебный поток...') print('Число активных потоков:', threading.active_count()) start_time = time.time() # Закомментированный блок выполняет те же действия, что и блок ниже его: # нужно ли использовать JOIN или потоки самостоятельно завершатся и освободят ресурсы? # Вариант 1 #------Работа служебных функций. Они запускаются в отдельных потоках--------- # threads = [] # threads.append(check_schedule()) # threads.append(clean_bot_messages()) # Пример функции, запускаемой в отдельном потоке # ........ другие функции # for item in threads: # item.join() #------Конец работы служебных функций--------- # Вариант 2 #------Работа служебных функций. Они тоже запускаются в отдельных потоках--------- check_schedule() clean_bot_messages() # Пример функции, запускаемой в отдельном потоке # ........ другие функции #------Конец работы служебных функций--------- time_delta = time.time() - start_time # время, затраченное на работу функций if notify: print(cur_time(), f'Служебные функции отработали за {time_delta} сек.') time.sleep(thread_sleep_time - time_delta) if notify: print(cur_time(), '- Служебный поток завершен...') def start_thread(): # Этот поток запускается при старте бота и работает постоянно, проблем с ним нет if const.service_thread_is_active(): print('Сервисный поток уже работает...') else: print('Сервисный поток еще не активен...') if const.get_thread_state() and not const.service_thread_is_active(): MyThread = threading.Thread(target = thread_jobs, name='AF_Bot_Service', args = (False,), daemon = True) MyThread.start() print('Сервисный поток запущен:', MyThread.name, ', ID:', MyThread.native_id) if __name__ == '__main__': start_thread() bot.infinity_polling(skip_pending = True)

Дополнительно:

Ответы:

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

Нужно решить такую задачу?

Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

Заказать помощь
Лучший ответ
1
Антон С. Ответ

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

Прежде всего, необходимо создать класс, который будет представлять собой отдельный поток. В этом классе нужно определить метод run(), который будет содержать код, который нужно выполнить в отдельном потоке. Затем создаем объекты этого класса и запускаем их в отдельных потоках.

Для того чтобы корректно завершить работу потоков, можно использовать метод join() для каждого потока. Этот метод блокирует основной поток до тех пор, пока поток, к которому он применен, не завершится. Таким образом, мы можем дождаться завершения всех запущенных потоков.

Пример кода:

class MyThread extends Thread {
    public function run() {
        // Код, который нужно выполнить в отдельном потоке
    }
}
 
$thread1 = new MyThread();
$thread2 = new MyThread();
 
$thread1->start();
$thread2->start();
 
$thread1->join();
$thread2->join();

class MyThread extends Thread { public function run() { // Код, который нужно выполнить в отдельном потоке } } $thread1 = new MyThread(); $thread2 = new MyThread(); $thread1->start(); $thread2->start(); $thread1->join(); $thread2->join();

В данном примере создается класс MyThread, который наследуется от класса Thread из расширения pthreads. Метод run() содержит код, который нужно выполнить в отдельном потоке. Затем создаются объекты этого класса и запускаются в отдельных потоках. Далее вызывается метод join() для каждого потока, чтобы дождаться их завершения.

Таким образом, используя механизм управления потоками и синхронизацию в PHP, можно корректно завершить работу нескольких потоков, запущенных из отдельного потока.

Другие ответы (0)

Пока нет других ответов. Будьте первым, кто поможет автору.

Ответить на вопрос

комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Вам также может быть интересно