4

Despite saving a reference to the QThread as self.lightsThread, stopping the QObject self.lightsWorker then starting self.lightsThread again caused the error

QThread: Destroyed while thread is still running

After stopping self.lightsWorker, must the QThread self.lightsThread be stopped too? If not, what seems to be the problem?

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import time



class Screen(QMainWindow):
    def __init__(self):
        super(Screen, self).__init__()
        self.initUI()

    def initUI(self):
        self.lightsBtn = QPushButton('Turn On')
        self.lightsBtn.setCheckable(True)  
        self.lightsBtn.setStyleSheet("QPushButton:checked {color: white; background-color: green;}")
        self.lightsBtn.clicked.connect(self.lightsBtnHandler)

        self.setCentralWidget(self.lightsBtn)

    def lightsBtnHandler(self):
        if self.lightsBtn.isChecked():
            self.startLightsThread()
        else:
            self.stopLightsThread()

    def startLightsThread(self):
        print 'start lightsThread'
        self.lightsThread = QThread()
        self.lightsWorker = LightsWorker()
        self.lightsWorker.moveToThread(self.lightsThread)
        self.lightsThread.started.connect(self.lightsWorker.work)
        self.lightsThread.start()


    def stopLightsThread(self):
        print 'stop lightsThread'
        self.lightsWorker.stop()



class LightsWorker(QObject):
    signalFinished = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)
        self._mutex = QMutex()
        self._running = True

    @pyqtSlot()
    def work(self):
        while self._running:
            print 'working'
            time.sleep(1)
        self.signalFinished.emit()

    @pyqtSlot()
    def stop(self):
        print 'Stopping'
        self._mutex.lock()
        self._running = False
        self._mutex.unlock()



app = QApplication(sys.argv)
window = Screen()
window.show()
sys.exit(app.exec_())
Nyxynyx
  • 61,411
  • 155
  • 482
  • 830
  • I have the same problem with my application. However, it's not really error and everything seems to be working. – Matho Apr 23 '17 at 19:35

2 Answers2

7

following the answer https://stackoverflow.com/a/32138213/7742341 after stopping lightWorker you should to quit from the thread and wait untill it is stopped

def stopLightsThread(self):
    print('stop lightsThread')
    self.lightsWorker.stop()
    self.lightsThread.quit()
    self.lightsThread.wait()
Community
  • 1
  • 1
Luchko
  • 1,123
  • 7
  • 15
  • This works! What is `.wait()` for? The example code in this question does not throw the error when `.wait()` is not used. – Nyxynyx Apr 23 '17 at 23:29
  • in some cases during the test when i clicked the button too fast the error was raised because the thread wasn't finished before I tryied to start it again – Luchko Apr 23 '17 at 23:38
  • I didn't need the .wait(), but I had to add the parent= to the QThread constructor. – rchuso Sep 17 '20 at 01:12
2

I had to face the same issue in C++, but the problem is the same.

The problem is that your QThread instance is deleted while the associated thread is still running. This can be really dangerous because the thread code execution is interrupted whitout any guarantee that the thread is ready to be deleted.

For example :

  • a thread control the execution and life time of an object (a worker)
  • a ressource is released in this object destructor (explicity or implicilty like when using QObject parent/child system)
  • since the thread execution is interrupted, the object will not be deleted

It lead to a memory leak and a ressource leak.

In your code, the worker is stopped, but not the working thread. I'm not python expert, but it also seems that your worker object is stopped but not deleted.

To properly stop your worker and thread, you should :

  • send a message to your worker to tell it to "stop working"
  • ask your thread to quit : it will post an "exit" message to the thread that will be processed after the worker execution
  • wait for your thread to stop

The last step is optionnal : if the thread and worker does not share ressources whith other object, you probably don't need to wait them to finish, just forget about them.

The only execption is that all your thread should be properly stopped before exiting the application : you should wait for all current running threads to stop before application exit.

For simple tasks, you should also consider using QtConcurrent framework.

Aurelien
  • 1,032
  • 2
  • 10
  • 24
  • Thanks this works, using the code Luchko posted. My code no longer throw the error after asing the thread to quit `self.lightsThread.quit()`. I assume `self.lightsThread.wait()` is for waiting for the thread to stop, if so why does leaving this out not cause the error to occur in my example code? – Nyxynyx Apr 23 '17 at 23:30
  • 1
    According to Qt documentation, "quit" post an event to the thread which tell it to stop processing. This event will be processed on the next thread's event loop. "wait" is for waiting the thread to be stopped. – Aurelien Apr 25 '17 at 06:08