Как безопасно останавливать потоки Python с помощью ввода ключа

#python #multithreading #python-multithreading

#python #многопоточность #python-многопоточность

Вопрос:

В моем приложении у меня есть два потока. Основной и «поток». therad генерирует некоторые данные и сохраняет их в списке python. Основной поток периодически копирует содержимое списка, сгенерированного «потоком». Оба потока имеют бесконечный цикл while. Моя цель — остановить оба потока, когда я нажимаю любую клавишу enter. Для достижения этой цели программа должна ожидать ввода с клавиатуры во время выполнения потоков. Я подумал, что мне нужен другой поток (скажем, менеджер), который только ожидает ввода с клавиатуры во время выполнения. Вот что я попробовал сначала:

 class managerThread(threading.Thread):
    is_running = True
    def __init__(self):
        threading.Thread.__init__(self)
        signal.signal(signal.SIGINT, self.kill_all)
        signal.signal(signal.SIGTERM, self.kill_all)
      
    def run(self):
        input("Press any key enter to stop: ")
        self.is_running = False
    
    def kill_all(self,signum, frame):
        print("Process ended with keyboard interrupt")
        self.is_running = False
        sys.exit(-1)

        
class thread(threading.Thread):
    mgr = managerThread()
    mgr.start()
    def __init__(self):
        threading.Thread.__init__(self)
    
    def run(self):
        while (self.mgr.is_running):
            print("this is acquiring data")
            sleep(2.5)
            
        self.mgr.join()
        print("manager stopped")


if __name__ == "__main__":
    thread = thread()
    thread.start()
    while (thread.mgr.is_running):
        print("this is copying data")
        sleep(3)
        
    thread.join()
    print("thread is stopped")
    sys.exit(0)
  

Приведенный выше код делает именно то, что я хочу сделать. Но это не кажется правильным. Менеджер управляет всеми остальными, но он создается в одном из подчиненных потоков. Другая проблема заключается в том, что можно попытаться создать несколько менеджеров в разных потоках. Этого следует строго избегать. Тогда я подумал, что менеджер должен наследоваться управляемыми классами. Вот что я попробовал:

 class managerThread(threading.Thread):
    is_running = True
    def __init__(self):
        threading.Thread.__init__(self)
        signal.signal(signal.SIGINT, self.kill_all)
        signal.signal(signal.SIGTERM, self.kill_all)
        self.start()
      
    def run(self):
        input("Press any key enter to stop: ")
        self.is_running = False
    
    def kill_all(self,signum, frame):
        print("Process ended with keyboard interrupt")
        self.is_running = False
        sys.exit(-1)

        
class thread(managerThread):
    def __init__(self):
        super().__init__()
        threading.Thread.__init__(self)
    
    def run(self):
        while (self.is_running):
            print("this is acquiring data")
            sleep(2.5)
            
        print("manager stopped")


if __name__ == "__main__":
    thread = thread()
    thread.start()
    while (thread.is_running):
        print("this is copying data")
        sleep(3)
        
    thread.join()
    print("thread is stopped")
    sys.exit(0)
  

Как видно из второго кода, основная часть та же. Я пытался сделать thread как дочерний элемент managerThread . Однако это не работает. Менеджер никогда не выполняет метод «run». Поэтому я не могу остановить другие потоки. Другая важная проблема заключается в том, что я не знаю, как остановиться super() join() . Я уверен, что совершаю ошибку в отношении наследования классов, но я не смог решить проблему, поскольку у меня не слишком много опыта работы с ООП, а потоки удвоили мое замешательство.

Примечание: меня не волнует синхронизация потоков.
Мои вопросы:
— Правильно ли создавать поток менеджера для безопасной остановки подчиненных потоков? Если нет, то как правильно?
— Почему второй код не работает? Что мне нужно изменить, чтобы заставить его работать?
— Почему родительский класс инициализируется, но он никогда не запускает метод «run»?
— Я считаю, что родительский класс никогда не запускается, но как я могу остановить его во втором коде, если он действительно запускается?
Спасибо.

Ответ №1:

Даже я не хотел использовать глобальную переменную для безопасной остановки всех потоков, я не мог найти решение без глобального флага запуска. Я также пытался передать изменяемую переменную в поток менеджера, но безуспешно. Вот рабочий набросок, который объясняет, как я решил свою проблему. Я надеюсь, что это поможет кому-то еще. Между тем, я был бы рад, если бы кто-нибудь предложил лучшее решение :).
Примечание: я не отлаживал его.

 import sys
import threading, queue
import signal
from time import sleep
import numpy as np

global_running = False

class managerThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        signal.signal(signal.SIGINT, self.kill_all)
        signal.signal(signal.SIGTERM, self.kill_all)
      
    def run(self):
        global is_running
        is_running = True
        input("Press any key enter to stop: ")
        is_running = False
        print("manager finished")
    
    def kill_all(self,signum, frame):
        global is_running
        is_running = False
        print("Process ended with keyboard interrupt")
        sys.exit(-1)

        
class thread(threading.Thread):   
    __mgr = managerThread() 
    __mgr.start()
    running = True
    def __init__(self):
        threading.Thread.__init__(self)
        self.myvar = 0
        self.queue = queue.Queue()
    
    def currentVar(self):
        var = np.empty(1)
        while (var.size<=1):
            var = self.queue.get()
        self.queue.task_done()
        return var
    
    def run(self):
        global is_running
        while (is_running):
            print("this is acquiring data")
            sleep(2.5)
            self.myvar = np.empty(5)
            self.queue.put(self.myvar)
        
        self.running = False
        self.__mgr.join()
        print("manager stopped")


if __name__ == "__main__":
    thread = thread()
    thread.start()
    while (thread.running):
        # var = thread.queue.get()
        var = thread.currentVar()
        print ("value is: ", var)
        # thread.queue.task_done()
     
    thread.join()
    print("thread is stopped")
    sys.exit(0)