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

Qt循环弹出对话框失败,线程运行时崩溃问题求助

Qt循环弹出对话框问题:首次弹出后无响应,线程销毁错误

需求与问题

  • 需求:循环弹出模态对话框,用户点击OK后自动弹出下一个,对话框需阻塞后续弹出操作。
  • 问题:仅首次对话框正常弹出,后续无反应,终端输出错误:QThread: 线程仍在运行时被销毁

最小复现代码

main.cpp

#include <QApplication>
#include <QObject>
#include <cstdio>
#include "MyClass.h"

int main(int argc, char *argv[])
{
  MyClass obj;
  QApplication a(argc, argv);
  obj.Init(&a);
  obj.start();
  return a.exec();
}

MyClass.cpp

#include "MyClass.h"

MyClass::MyClass() { }

void MyClass::openDialog()
{
  // Create and execute the dialog box
  QMessageBox messageBox;
  messageBox.setText("Dialog Box");
  messageBox.exec();

  // Signal that the dialog box has closed
  emit dialogClosed();
}

void MyClass::run() {
  while (1){
    printf("PRESS OK TO CONTINUE LOOPING...\n");

    // Open dialog box within the GUI thread
    QMetaObject::invokeMethod(this, "openDialog", Qt::QueuedConnection);

    // Wait for dialog to close
    QEventLoop loop;
    connect(this, &MyClass::dialogClosed, &loop, &QEventLoop::quit);
    loop.exec();

    printf("LOOP ENDED.\n");
  }
}

int MyClass::Init(QCoreApplication * App)
{
}

MyClass.h

#include <QThread>
#include <QCoreApplication>
#include <QMessageBox>

#ifndef MYCLASS_H
#define MYCLASS_H

class MyClass : public QThread
{
  Q_OBJECT

private:
  void run();

signals:
  void dialogClosed();

public:
  MyClass();
  int Init(QCoreApplication *App);

public slots:
  void openDialog();

};

#endif // MYCLASS_H

MyClass.pro

QT += core network
QT += widgets
CONFIG += c++11
SOURCES += main.cpp MyClass.cpp
HEADERS += MyClass.h

问题原因分析

  1. 线程销毁错误:MyClass对象是main函数的栈局部变量,当QApplication::exec()返回(比如用户关闭应用),main函数结束时会销毁该对象,但此时子线程仍在无限循环运行,触发线程销毁错误。
  2. 无正常线程退出机制:原代码用while(1)无限循环,无法正常终止线程,导致对象销毁时线程仍在运行。
  3. 潜在的事件队列延迟:main函数先启动子线程再进入主线程事件循环,虽然Qt会缓存跨线程调用,但事件处理顺序可能导致后续对话框无法及时弹出。

修复方案

修复后的代码

main.cpp

#include <QApplication>
#include <cstdio>
#include "MyClass.h"

int main(int argc, char *argv[])
{
  QApplication a(argc, argv);
  // 使用堆分配避免栈销毁时线程仍在运行
  MyClass *obj = new MyClass();
  obj->Init(&a);
  obj->start();
  
  int ret = a.exec();
  
  // 退出前终止线程并等待其结束
  obj->stop();
  // 手动触发一次dialogClosed,避免线程阻塞在loop.exec()
  emit obj->dialogClosed();
  obj->wait();
  delete obj;
  
  return ret;
}

MyClass.h

#include <QThread>
#include <QCoreApplication>
#include <QMessageBox>

#ifndef MYCLASS_H
#define MYCLASS_H

class MyClass : public QThread
{
  Q_OBJECT

private:
  void run() override;
  bool m_stop = false; // 添加线程停止标志

signals:
  void dialogClosed();

public:
  MyClass();
  int Init(QCoreApplication *App);
  void stop() { m_stop = true; } // 线程停止方法

public slots:
  void openDialog();

};

#endif // MYCLASS_H

MyClass.cpp

#include "MyClass.h"

MyClass::MyClass() { }

void MyClass::openDialog()
{
  QMessageBox messageBox;
  messageBox.setText("Dialog Box");
  messageBox.exec();
  emit dialogClosed();
}

void MyClass::run() {
  while (!m_stop){
    printf("PRESS OK TO CONTINUE LOOPING...\n");

    // 检查调用是否成功
    bool invoked = QMetaObject::invokeMethod(this, "openDialog", Qt::QueuedConnection);
    if (!invoked) {
      printf("Failed to invoke openDialog\n");
      break;
    }

    QEventLoop loop;
    // 使用SingleShotConnection确保连接只触发一次
    connect(this, &MyClass::dialogClosed, &loop, &QEventLoop::quit, Qt::SingleShotConnection);
    loop.exec();

    printf("LOOP ENDED.\n");
  }
}

int MyClass::Init(QCoreApplication * App)
{
  return 0; // 空实现返回有效状态码
}

修复说明

  1. 堆分配线程对象:避免栈对象销毁时线程仍在运行,解决线程销毁错误。
  2. 添加线程停止机制:用m_stop标志控制循环,配合stop()方法实现线程正常终止。
  3. 优化事件调用:检查invokeMethod的返回值,确保对话框调用成功;使用Qt::SingleShotConnection避免重复连接问题。
  4. 正确的线程终止流程:在main函数退出前,先停止线程,手动触发dialogClosed信号解除线程阻塞,再等待线程结束后销毁对象。

内容的提问来源于stack exchange,提问作者Hai Nguyen Van

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 01:55:04