为何关闭Qt GUI时会出现QThread: Destroyed while thread is still running错误?
问题原因与解决方案
核心问题
你遇到的报错本质是线程并未真正被终止,原因有两个:
- 误用了Qt的线程ID:
QThread.currentThreadId()返回的是底层操作系统的线程ID,而ctypes.pythonapi.PyThreadState_SetAsyncExc需要的是Python层面的线程标识符,两者不匹配,导致异常根本没抛到目标线程里,线程还在后台循环运行。 - 未正确处理
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
相关产品推荐
相关产品推荐

