Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PyQt QThread: Destroyed while thread is still running

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_())
like image 851
Nyxynyx Avatar asked Feb 05 '23 14:02

Nyxynyx


2 Answers

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()
like image 175
Luchko Avatar answered Feb 12 '23 16:02

Luchko


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.

like image 23
Aurelien Avatar answered Feb 12 '23 17:02

Aurelien