如何正确退出PySide2多线程GUI应用程序?
解决PySide2关闭窗口后进程挂起的问题
从你的描述和代码来看,问题的核心是后台线程没有正常终止,导致Python进程无法完全退出,终端卡住无法恢复命令提示符。下面一步步分析原因和解决办法:
为什么会出现这个情况?
- 非守护线程阻止进程退出:你用
QThreadPool启动的ViewWorker和SerialWorker(应该是继承QRunnable的类),默认情况下运行的线程不是守护线程。Python进程会等待所有非守护线程执行完毕才会终止,哪怕主事件循环已经结束。 - 队列阻塞导致线程挂起:如果你的线程用了无参数的
queue.get(),它会一直阻塞等待队列数据,即使主窗口关闭,线程也无法跳出循环,一直处于挂起状态。 - 未释放资源(比如串口):如果
SerialWorker打开了串口但没正确关闭,未释放的系统资源也可能拖慢甚至阻止进程退出。
具体解决步骤
1. 给Worker添加终止标志+设置守护线程
修改你的Worker类,添加一个is_running标志控制循环,同时把线程设为守护线程,确保主进程退出时能强制终止线程:
import queue import threading from PySide2.QtCore import QRunnable class ViewWorker(QRunnable): def __init__(self, serial_queue, canvas, text_edit): super().__init__() self.serial_queue = serial_queue self.canvas = canvas self.text_edit = text_edit self.is_running = True # 控制线程运行的标志 def run(self): # 将当前线程设为守护线程 threading.current_thread().daemon = True while self.is_running: try: # 用timeout避免无限阻塞,每隔1秒检查一次是否要退出 data = self.serial_queue.get(timeout=1) # 你的数据解析、绘图逻辑 self.process_and_draw(data) except queue.Empty: # 队列为空时继续循环,检查is_running标志 continue # 线程退出前的清理操作(比如关闭绘图资源) self.cleanup() # SerialWorker同理添加is_running标志和守护线程设置
2. 在主窗口关闭时通知线程退出
重写MainWindow的closeEvent方法,在窗口关闭时主动通知线程停止,并等待线程池任务完成,同时释放串口资源:
class MainWindow(QMainWindow, Ui_MainWindow): def __init__(self, *args, **kwargs): Ui_MainWindow.__init__(self) QMainWindow.__init__(self) # 原有UI初始化代码... self.serialRxQu = Queue() self.viewWo = ViewWorker(self.serialRxQu, self.canvas, self.textEdit) self.serialWo = SerialWorker(self.serialRxQu, self.textEdit) self.threadpool = QThreadPool() self.threadpool.start(self.viewWo) def closeEvent(self, event): # 通知所有Worker停止运行 self.viewWo.is_running = False self.serialWo.is_running = False # 等待线程池中的所有任务完成(可选,根据你的业务需求) self.threadpool.waitForDone(3000) # 最多等待3秒 # 关闭串口(如果SerialWorker里有打开串口的逻辑) if hasattr(self.serialWo, 'serial') and self.serialWo.serial.isOpen(): self.serialWo.serial.close() # 接受窗口关闭事件 event.accept()
3. 检查队列使用方式
确保线程中使用queue.get(timeout=xxx)而不是无参数的queue.get(),这样线程不会一直阻塞在获取队列数据上,能定期检查is_running标志,及时退出循环。
关于app.exec_()和sys.exit()的说明
你分开调用时能打印"I pass here!",说明主事件循环已经正常结束,但进程没退出——这完全符合我们的分析:非守护线程还在运行,Python要等它们结束才会终止进程。添加守护线程和终止标志后,这个问题就会解决。
按照上面的方法修改后,关闭主窗口时,线程会主动退出循环,守护线程的设置也会让主进程无需等待线程完全结束就能终止,终端就能正常恢复命令提示符了。
内容的提问来源于stack exchange,提问作者Angel DC
相关产品推荐
相关产品推荐

