Managing QThread References to Avoid Runtime Crashes in PyQt5

When integrating background processing into a PyQt5 application, instantiating QThread inside a local scope—such as a button-click handler—often triggers intermittent crashes or segmentation faults. The underlying cause is Python’s garbage collector reclaiming the QThread wrapper object once the local variable goes out of scope, even though the native thread is still executing.

To keep the thread alive, bind the instance to a long-lived parent object, typically the main window. The following example constructs the interface programmatically and retains the worker reference via self:

import sys
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QApplication, QMainWindow, QTextEdit, QPushButton


class ComputationTask(QThread):
    result_ready = pyqtSignal(str)

    def __init__(self, identifier):
        super().__init__()
        self.identifier = identifier

    def run(self):
        total = 0
        for i in range(1800000000):
            total += i
        self.result_ready.emit(f"Task {self.identifier} finished with total {total}")


class PrimaryWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QThread Lifetime Example")
        self.resize(500, 320)

        self.output_area = QTextEdit(self)
        self.output_area.setGeometry(40, 30, 320, 220)

        self.launch_button = QPushButton("Execute", self)
        self.launch_button.setGeometry(400, 40, 80, 30)
        self.launch_button.clicked.connect(self._start_background_work)

        # Preserve the thread as an instance attribute to prevent GC destruction
        self.background_job = ComputationTask("Alpha")
        self.background_job.result_ready.connect(self._display_output)

    def _start_background_work(self):
        if not self.background_job.isRunning():
            self.background_job.start()

    def _display_output(self, text):
        current = self.output_area.toPlainText()
        self.output_area.setPlainText(f"{current}\n{text}")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = PrimaryWindow()
    win.show()
    sys.exit(app.exec_())

Storing the thread in self.background_job ensures the Python object persists for the entire execution window. When mulitple independent workers are reuqired, maintain them in a collection and prune entries only after receiving their finished signal.

Tags: PyQt5 QThread multithreading python Qt

Posted on Sun, 17 May 2026 07:59:24 +0000 by hessodreamy