nullstellensatz
nullstellensatz

Reputation: 774

PySide QThread.terminate() causing fatal python error

I am using PySide version 1.2.2, which wraps the Qt v4.8 framework. I am in a situation where I have to choose between having my application wait for a QThread that I no longer need to exit normally (it is quite possible that the thread will block indefinitely), and giving the unresponsive thread a grace period (of several seconds), then calling QThread.terminate() on it. Though I wish I could, I cannot let the QThread object go out of scope while the underlying thread is still running, since this will throw the error "QThread: Destroyed while thread is still running" and almost surely cause a segfault.

Please note that I am aware that terminating QThreads is dangerous and highly discouraged. I am just trying to explore my options here.

When I try to terminate a thread however, my application crashes with the following error:

Fatal Python error: This thread state must be current when releasing

You can try this out yourself by copy/pasting and running the following code:

from PySide import QtCore, QtGui

class Looper(QtCore.QThread):
    """QThread that prints natural numbers, one by one to stdout."""
    def __init__(self, *args, **kwargs):
        super(Looper, self).__init__(*args, **kwargs)
        self.setTerminationEnabled(True)

    def run(self):
        i = 0
        while True:
            self.msleep(100)
            print(i)
            i += 1

# Initialize and start a looper.                                                                      
looper = Looper()
looper.start()

# Sleep main thread for 5 seconds.                                                                    
QtCore.QThread.sleep(5)

# Terminate looper.                                                                                   
looper.terminate()

# After calling terminate(), we should call looper.wait() or listen
# for the QThread.terminated signal, but that is irrelevant for
# the purpose of this example.

app = QtGui.QApplication([])
app.exec_()

How do you properly terminate QThreads in Python?

I reckon that the error I am getting has got something to do with releasing of the Global Interpreter Lock, but I am not sure exactly what is going wrong, and how to fix it.

Upvotes: 5

Views: 5232

Answers (1)

ekhumoro
ekhumoro

Reputation: 120588

It seems that the error may be specific to PySide: running your example with PyQt4 does not produce any errors at all.

As for the general issue of how to terminate a QThread safely: it entirely depends on how much control you have over the work that is being done in the thread. If it is effectively a loop where you can periodically check a flag, then the solution is simple:

class Looper(QtCore.QThread):
    ...         

    def interrupt(self):
        self._active = False

    def run(self):
        i = 0
        self._active = True
        while self._active:
            self.msleep(100)
            print(i)
            i += 1

app = QtGui.QApplication([])

looper = Looper()
looper.finished.connect(app.quit)
looper.start()

QtCore.QTimer.singleShot(3000, looper.interrupt)

app.exec_()

The thread will finish cleanly once the run method returns, so you must find some mechanism to allow that happen. If you can't do that (perhaps because the work being done in the thread is largely outside of your control), you should probably consider switching to a multiprocessing approach instead.

Upvotes: 2

Related Questions