Как правильно завершить QThread из приложения с графическим интерфейсом?

#python #multithreading #qt #pyqt

#python #многопоточность #qt #pyqt

Вопрос:

Я пытался использовать self.terminate() в классе QThread, а также self.thread.terminate() в классе GUI. Я также пытался использовать self.wait() оба случая. Однако возможны два сценария:

1) Поток вообще не завершается, и графический интерфейс зависает в ожидании завершения потока. Как только поток завершается, графический интерфейс размораживается, и все возвращается в нормальное русло.

2) Поток действительно завершается, но в то же время он замораживает все приложение.

Я также пробовал использовать self.thread.exit() . Никакой радости.

Чтобы еще раз уточнить, я пытаюсь реализовать кнопку прерывания пользователя в графическом интерфейсе, которая завершила бы выполнение потока в любой момент времени.

Заранее спасибо.

Редактировать:

Вот run() метод:

 def run(self):
    if self.create:
        print "calling create f"
        self.emit(SIGNAL("disableCreate(bool)"))
        self.create(self.password, self.email)
        self.stop()            
        self.emit(SIGNAL("finished(bool)"), self.completed)

def stop(self):
     #Tried the following, one by one (and all together too, I was desperate):
     self.terminate()
     self.quit()
     self.exit()
     self.stopped = True
     self.terminated = True
     #Neither works
 

И вот метод класса GUI для прерывания потока:

 def on_abort_clicked(self):
     self.thread = threadmodule.Thread()
     #Tried the following, also one by one and altogether:
     self.thread.exit()
     self.thread.wait()
     self.thread.quit()
     self.thread.terminate()
     #Again, none work
 

Комментарии:

1. Примечание: self.terminate === (self.terminated = true), не более того. Для 99% всех реализаций.

2. Спасибо, но это на самом деле не помогает решить проблему.

3. Есть только один способ безопасно завершить поток — позволить ему завершить все ИТ-задачи. Чтобы выполнить это, вы должны проверить thread.terminated перед выполнением сложных задач.

4. Ну, это, конечно, не то, что я ищу.

Ответ №1:

Из документации Qt для QThread::terminate:

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

Вероятно, гораздо лучше переосмыслить свою стратегию обработки потоков таким образом, чтобы вы могли, например, использовать QThread::quit(), чтобы сигнализировать потоку о чистом завершении, а не пытаться заставить поток завершиться таким образом. На самом деле вызов thread.exit() из потока должен делать это в зависимости от того, как вы реализовали run() . Если вы хотите поделиться кодом для вашего метода выполнения потока, который может подсказать, почему он не работает.

Комментарии:

1. Хм. И я предполагаю self.create() , что метод является трудоемким методом в потоке? Обычно то, что вы бы сделали там, выглядит примерно так while not self.terminated() { do slow operation parts.. } . К сожалению, я не очень хорошо знаю Python, но, похоже, здесь есть руководство по потоковой передаче, которое может помочь

2. Я тоже пробовал это. Это несколько сработало (графический интерфейс не зависал, но поток также не завершался). Однако только что произошло самое странное. Я удалил весь def stop(self) код в потоке и def on_abort_clicked(self) код в графическом интерфейсе и переписал его. Я просто ввел self.terminate() функцию потока stop() и сделал self.thread.stop() это в классе GUI. Это работает как шарм. AFAIK, это первое, что я попробовал, и раньше это не работало. Вероятно, я что-то пропустил.

Ответ №2:

Это то, что я сделал:

 class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    stop_flag = 1
    ...

#=========500 ms class ===================
class Timer500msThread(QThread):
    signal_500ms = pyqtSignal(str) 
    ....
    def timer500msProcess(self):
        if MainWindow.stop_flag == 0 :
            self.timer_500ms.stop()
#==========
#=========Main Window ===================
        
app = QtWidgets.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
MainWindow.stop_flag=0      #this sets the flag to 0 and when the next 500ms triggers the 
                               #the thread ends
print("Program Ending")

 

Ответ №3:

У меня была похожая проблема , и я решил ее с помощью pyqtSignals and pyqtSlots . Вы можете создать a pyqtSignal в своем MainWindow-классе и использовать aboutToQuit -function для своего QApplication экземпляра. Затем вы соединяете эту aboutToQuit функцию с другой, которая передает сигнал в слот в вашем отдельном потоке. Затем вы можете определить stop() функцию в этом потоке, которая запускается при отправке сигнала. В этом случае поток не будет завершаться во время его работы.

MainWindow:

 class mainSignals(QObject):
    isClosed = pyqtSignal()

class mainwindow(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(mainwindow, self).__init__()
        self.mainSignal = mainSignals()
        ...
        ...

if __name__ = "__main__":
    # This function runs if the application is closed. 
    # (app.aboutToQuit.connect(windowClose))
    def windowClose():
        window.mainSignal.isClosed.emit() # Emit the signal to the slot in (sepThread)


    app = QApplication(sys.argv)
    app.aboutToQuit.connect(windowClose)
    window = mainwindow()
    window.show()
    sys.exit(app.exec())
 

Септред:

 class sepThread(QRunnable):
    def __init__(self, parent):
        super(sepThread,self).__init__()
        self._parent = parent
        self.mainOpen = True
        self._parent.mainSignal.isClosed.connect(self.stopThread)
        # If the signal was emitted by the Mainapplication 
        # the stopThread function runs and set the mainOpen-attribute to False

    def stopThread(self):
        self.mainOpen = False
    
        
    def run(self):
        while self.mainOpen == True:
            # Do something in a loop while mainOpen-attribute is True
            ...
            ...