QThread运行ZeroMQ关闭对话框后提示线程仍在运行时被销毁如何解决
问题原因
- Qt默认开启
quitOnLastWindowClosed配置,关闭最后一个活跃窗口时会直接退出应用事件循环。你当前没有设置主窗口,关闭弹出的对话框后,QApplication.exec()就会执行结束,App实例随即被销毁,其持有的zmq_thread成员也会被回收,但此时ZMQ线程还在无限循环中运行,就触发了QThread: Destroyed while thread is still running错误,后面的异常码是线程资源被提前释放导致的访问冲突。 - ZMQ的
recv()默认是阻塞调用,即使你给线程加了退出标志,只要没有新消息进来,线程会一直卡在recv调用处,无法正常退出。 - 你使用了ZMQ的REP套接字,协议要求收到REQ消息后必须返回回复,否则后续套接字状态会异常,属于潜在的崩溃隐患。
解决方案
- 关闭Qt的“最后一个窗口关闭即退出”逻辑,适配你后续做托盘运行的需求,关闭对话框不会触发整个程序退出。
- 给ZMQ线程添加安全退出逻辑:增加运行状态标志位替换死循环,给ZMQ套接字设置接收超时避免永久阻塞,程序退出时主动停止线程并等待线程退出完成。
- 修复REP套接字的协议逻辑,收到消息后返回合规回复。
修改后的完整代码
import sys import zmq from PyQt5.QtCore import QThread, Signal from PyQt5 import QtWidgets # 此处保留你原有Ui_MyDialog的逻辑即可 class Ui_MyDialog: def setupUi(self, Dialog): Dialog.resize(200, 100) self.ButtonOK = QtWidgets.QPushButton("确定", Dialog) self.ButtonOK.setGeometry(50, 30, 100, 30) class ZMQThread(QThread): msg_received = Signal() def __init__(self): super(ZMQThread, self).__init__() self.running = True self.context = zmq.Context() self.socket = self.context.socket(zmq.REP) self.socket.setsockopt(zmq.RCVTIMEO, 1000) # 设置接收超时1秒 self.socket.bind("tcp://*:3344") def run(self): while self.running: try: message = self.socket.recv() self.socket.send(b'ok') # REP套接字必须返回回复 self.msg_received.emit() except zmq.Again: # 超时直接继续循环判断退出标志 continue def stop(self): self.running = False self.wait() # 等待线程完全退出 # 清理ZMQ资源 self.socket.close() self.context.term() class MyWindow(QtWidgets.QDialog): def __init__(self): super(MyWindow, self).__init__() self.ui = Ui_MyDialog() self.ui.setupUi(self) self.ui.ButtonOK.clicked.connect(self.ok) def ok(self): self.accept() class App: def __init__(self): self.qtapp = QtWidgets.QApplication([]) # 关闭最后一个窗口不退出应用 self.qtapp.setQuitOnLastWindowClosed(False) self.my_window = MyWindow() self.zmq_thread = ZMQThread() self.zmq_thread.msg_received.connect(self.show_my_window) self.zmq_thread.start() # 绑定应用退出信号,退出前先停止ZMQ线程 self.qtapp.aboutToQuit.connect(self.zmq_thread.stop) def show_my_window(self): self.my_window.show() if __name__ == '__main__': my_gui = App() sys.exit(my_gui.qtapp.exec())
内容的提问来源于stack exchange,提问作者Z T
相关产品推荐
相关产品推荐

