You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何关闭Qt GUI时会出现QThread: Destroyed while thread is still running错误?

问题原因与解决方案

核心问题

你遇到的报错本质是线程并未真正被终止,原因有两个:

  1. 误用了Qt的线程ID:QThread.currentThreadId()返回的是底层操作系统的线程ID,而ctypes.pythonapi.PyThreadState_SetAsyncExc需要的是Python层面的线程标识符,两者不匹配,导致异常根本没抛到目标线程里,线程还在后台循环运行。
  2. 未正确处理PyThreadState_SetAsyncExc的返回值,可能导致异常设置失败或资源泄漏。

修正方案

1. 获取正确的Python线程ID

在Thread_Test1.run()方法中,用threading.get_ident()替换currentThreadId(),获取Python线程的唯一标识符。

2. 修复stop_thread()方法

处理PyThreadState_SetAsyncExc的返回值,确保异常正确抛出,同时清理可能的残留状态:

  • 返回值为0:说明线程ID无效(线程已结束或不存在)
  • 返回值大于1:需要调用PyThreadState_SetAsyncExc(thread_id, 0)清除异常,避免影响其他线程

3. 窗口关闭时确保线程终止

重写MyWindow.closeEvent方法,在窗口关闭前检查线程状态,若线程仍在运行,则终止并等待其结束,避免销毁QThread对象时线程还在运行。

修正后的完整代码

import ctypes
import sys
import threading
import time

from PyQt5 import QtWidgets, QtCore
from PyQt5.QtCore import QThread
from PyQt5.QtWidgets import *

class MyWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.Button_state = True
        self.pb_start.clicked.connect(self.click1_function)
        self.test = None  # 初始化线程对象

    def click1_function(self):
        if self.Button_state:
            self.Button_state = False
            self.pb_start.setText('Stop')
            self.test = Thread_Test1(self)
            self.test.daemon = True
            self.test.start()
        else:
            self.Button_state = True
            self.pb_start.setText('Start')
            if self.test and self.test.isRunning():
                self.test.stop_thread()
                self.test.wait()  # 等待线程真正结束

        print('check')

    def setupUi(self, QMainWindow):
        self.resize(500, 500)
        self.centralwidget = QtWidgets.QWidget(QMainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.pb_start = QtWidgets.QPushButton(self.centralwidget)
        self.pb_start.setGeometry(QtCore.QRect(100, 100, 100, 100))
        self.pb_start.setObjectName("pb_start")
        self.pb_start.setText("Start")
        QMainWindow.setCentralWidget(self.centralwidget)

    def closeEvent(self, event):
        # 窗口关闭前确保线程终止
        if self.test and self.test.isRunning():
            self.test.stop_thread()
            self.test.wait()
        event.accept()

class Thread_Test1(QThread):
    def __init__(self, parent):
        super().__init__(parent)
        self.parent = parent
        self.test_thread_id = 0  # 初始化线程ID

    def run(self):
        i = 0
        self.test_thread_id = threading.get_ident()  # 获取Python线程ID
        try:
            while True:
                print(i)
                i += 1
                time.sleep(1)
                print("END")
        except SystemExit:
            # 捕获SystemExit,确保线程正常退出
            print("线程被终止")

    def stop_thread(self):
        if self.test_thread_id == 0:
            print("线程ID无效")
            return
        # 抛出SystemExit异常终止线程
        res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
            self.test_thread_id, ctypes.py_object(SystemExit)
        )
        if res == 0:
            print("未找到目标线程")
        elif res > 1:
            # 清除异常,防止影响其他线程
            ctypes.pythonapi.PyThreadState_SetAsyncExc(self.test_thread_id, 0)
            print("异常设置过多,已清理")

if __name__ == "__main__":
    app = QApplication(sys.argv)
    myApp = MyWindow()
    myApp.show()
    app.exec_()

额外说明

  • 即使使用强制终止的方式,也要确保线程内的资源能被正确释放(比如在run()里捕获异常做清理)。
  • test.wait()方法会阻塞当前线程(GUI线程)直到目标线程结束,避免QThread对象被销毁时线程还在运行。

内容的提问来源于stack exchange,提问作者wookidookik123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 06:26:28