详解qthread destroyed while thread is still running

举报
皮牙子抓饭 发表于 2024/01/28 20:26:27 2024/01/28
【摘要】 详解qthread destroyed while thread is still running在 Qt 编程中,我们经常使用 QThread 类来创建多线程应用程序。但在使用 QThread 时,有时会遇到这样的报错信息:QThread: Destroyed while thread is still running。这个错误表示在销毁 QThread 对象时,线程仍然在运行。 造成这个...

详解qthread destroyed while thread is still running

在 Qt 编程中,我们经常使用 QThread 类来创建多线程应用程序。但在使用 QThread 时,有时会遇到这样的报错信息:QThread: Destroyed while thread is still running。这个错误表示在销毁 QThread 对象时,线程仍然在运行。 造成这个错误的原因是在 QThread 对象被销毁之前,没有正确停止线程的执行。为了解决这个问题,我们需要在销毁 QThread 对象之前,显式地停止线程。

错误示例

让我们通过一个简单的示例来演示这个错误。假设我们有一个 Worker 类,它继承自 QObject 并且在一个单独的线程中执行耗时的操作:

cppCopy code
class Worker : public QObject
{
    Q_OBJECT
public:
    Worker() {}
public slots:
    void doWork() {
        for (int i = 0; i < 5; ++i) {
            qDebug() << "Working...";
            QThread::sleep(1);
        }
    }
};

现在,我们在主线程中创建一个 QThread 对象,并将 Worker 对象移动到新线程中执行:

cppCopy code
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QThread thread;
    Worker* worker = new Worker();
    worker->moveToThread(&thread);
    QObject::connect(&thread, &QThread::started, worker, &Worker::doWork);
    
    thread.start();
    
    qDebug() << "Thread started...";
    
    // 销毁 QThread 对象
    delete worker;
    // 销毁 QThread 对象之后的一些操作
    
    return app.exec();
}

这段代码在创建 QThread 对象后,调用了 delete worker 来销毁 Worker 对象。然而,由于线程的执行可能还没有完成,所以在销毁 QThread 对象时,会输出报错信息 QThread: Destroyed while thread is still running

解决方案

为了避免这个错误,我们应该显式地停止线程的执行,然后再销毁 QThread 对象。在 Qt 中,我们可以通过发送一个自定义的信号给 Worker 对象,来请求它停止执行。然后,我们可以在 QThread 对象的销毁函数中捕捉这个信号并等待线程停止。 首先,我们需要修改 Worker 类,添加一个新的信号 stopWork() 来通知线程停止运行:

cppCopy code
class Worker : public QObject
{
    Q_OBJECT
public:
    Worker() {}
public slots:
    void doWork() {
        for (int i = 0; i < 5; ++i) {
            qDebug() << "Working...";
            QThread::sleep(1);
            
            // 在每次迭代之后检查是否收到停止信号
            if (QThread::currentThread()->isInterruptionRequested()) {
                qDebug() << "Stop signal received. Stopping...";
                break;
            }
        }
        
        // 线程执行完毕后,发送 finished 信号
        emit finished();
    }
    
signals:
    void finished();
    void stopWork();
};

然后,我们在 main 函数中修改代码,当要销毁 QThread 对象时,发送停止信号并等待线程停止:

cppCopy code
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QThread thread;
    Worker* worker = new Worker();
    worker->moveToThread(&thread);
    QObject::connect(&thread, &QThread::started, worker, &Worker::doWork);
    
    thread.start();
    
    qDebug() << "Thread started...";
    
    // 发送停止信号并等待线程停止
    QObject::connect(worker, &Worker::stopWork, &thread, &QThread::requestInterruption);
    QObject::connect(worker, &Worker::finished, &thread, &QThread::quit);
    
    // 销毁 QThread 对象之前等待线程停止
    QObject::connect(&thread, &QThread::finished, worker, &QObject::deleteLater);
    thread.quit();  // 请求线程停止
    thread.wait();  // 等待线程停止
    
    qDebug() << "Thread stopped...";
    
    // 销毁 QThread 对象之后的一些操作
    
    return app.exec();
}

在这段修改后的代码中,我们通过连接 Worker::stopWorkQThread::requestInterruption 信号,请求线程停止。然后,通过连接 Worker::finishedQThread::quit 信号,等待线程执行完毕后退出。最后,在销毁 QThread 对象前,我们连接了 QThread::finished 信号和 QObject::deleteLater 槽函数,以确保在线程停止后才删除 Worker 对象。 现在,我们运行这段代码,就可以避免报错信息 QThread: Destroyed while thread is still running,程序能够顺利退出。




有一个文本编辑器的应用程序,用户可以在文本编辑器中打开和编辑大型文件。在保存文件时,由于保存操作可能需要花费一些时间,我们希望在后台执行保存操作,以保持用户界面的流畅性。 以下是使用多线程执行文件保存操作的示例代码:

cppCopy code
#include <QApplication>
#include <QMessageBox>
#include <QThread>
#include <QFile>
#include <QTextStream>
// 文件保存任务的工作线程
class SaveWorker : public QObject
{
    Q_OBJECT
public slots:
    void saveFile(const QString& filePath, const QString& content)
    {
        // 在工作线程中执行保存文件操作
        QFile file(filePath);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            emit saveFailed("无法保存文件");
            return;
        }
        QTextStream stream(&file);
        stream << content;
        file.close();
        emit saveCompleted();
    }
signals:
    void saveCompleted();
    void saveFailed(const QString& errorMessage);
};
// 主窗口
class MainWindow : public QWidget
{
    Q_OBJECT
public:
    MainWindow()
    {
        // 设置界面和布局...
        connect(&saveButton, &QPushButton::clicked, this, &MainWindow::saveFile);
        connect(&saveWorker, &SaveWorker::saveCompleted, this, &MainWindow::handleSaveCompleted);
        connect(&saveWorker, &SaveWorker::saveFailed, this, &MainWindow::handleSaveFailed);
    }
private slots:
    void saveFile()
    {
        QString filePath = ...;  // 获取文件路径
        QString content = ...;  // 获取文件内容
        // 创建保存文件的工作线程
        QThread* thread = new QThread;
        saveWorker.moveToThread(thread);
        connect(thread, &QThread::started, &saveWorker, [this, filePath, content]() {
            saveWorker.saveFile(filePath, content);
        });
        connect(&saveWorker, &SaveWorker::saveCompleted, thread, &QThread::quit);
        connect(&saveWorker, &SaveWorker::saveFailed, thread, &QThread::quit);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);
        // 启动工作线程
        thread->start();
    }
    void handleSaveCompleted()
    {
        QMessageBox::information(this, "保存完成", "文件保存成功");
    }
    void handleSaveFailed(const QString& errorMessage)
    {
        QMessageBox::critical(this, "保存失败", errorMessage);
    }
private:
    QPushButton saveButton;
    SaveWorker saveWorker;
};
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWindow mainWindow;
    mainWindow.show();
    return app.exec();
}

在这个示例中,我们首先定义了一个 SaveWorker 类,它继承自 QObject。该类包含一个 saveFile 槽函数,用于在工作线程中执行保存文件操作。保存文件时,它会将文件内容写入到指定的文件路径。如果保存失败,则通过 saveFailed 信号发送错误消息,否则通过 saveCompleted 信号表示保存完成。 主窗口类 MainWindow 中,我们连接了保存按钮的 clicked 信号到 saveFile 槽函数。在 saveFile 槽函数中,我们获取文件路径和内容,并创建一个新的 QThread 对象作为工作线程。然后,我们将 saveWorker 对象移动到工作线程中,并连接信号和槽函数,确保在保存操作完成或失败时能够正确处理。最后,我们启动工作线程。 当文件保存操作完成时,工作线程会发送 saveCompleted 信号,主窗口会弹出一个消息框显示保存成功的消息。如果保存操作失败,工作线程会发送 saveFailed 信号,主窗口会弹出一个消息框显示保存失败的消息。



QThread是Qt框架提供的一个类,用于支持多线程编程。它封装了线程相关的功能,包括线程的创建、启动、停止、等待和同步等操作。 QThread类提供了以下主要功能:

  1. 线程的创建和管理:通过实例化QThread类,可以创建一个新的线程对象。这个线程对象可以通过调用start()函数启动,从而执行run()函数中的代码。QThread类提供了一系列函数用于管理线程的状态,如isRunning()函数用于检查线程是否正在运行。
  2. 信号与槽机制:QThread类继承自QObject,因此它可以发送和接收信号。在多线程编程中,我们通常使用信号与槽机制实现线程间的通信。QThread类提供了一些信号,如started()和finished()信号,分别在线程启动和结束时发出。我们可以连接这些信号到其他对象的槽函数,以实现线程间的数据传递和同步。
  3. 线程同步:QThread类提供了一些函数可用于线程同步,如wait()函数用于等待线程执行完成,terminate()函数用于强制终止线程的执行。此外,QThread类还提供了一些静态函数作为工具,如sleep()函数用于使当前线程休眠一段时间。
  4. 事件循环:在一个QThread对象中,可以通过调用exec()函数进入事件循环,从而可以处理事件和信号。这在需要在线程中处理GUI事件或使用Qt的其他事件驱动功能时非常有用。 使用QThread类创建和管理线程的典型步骤如下:
  5. 创建QThread对象:通过实例化QThread类创建一个线程对象。
  6. 编写线程执行逻辑:继承QThread类,并重写其run()函数,在run()函数中编写线程的执行逻辑。
  7. 启动线程:通过调用线程对象的start()函数,启动线程并执行run()函数中的代码。
  8. 进行线程同步:根据需要使用QThread类提供的函数进行线程同步,如等待线程执行完成。
  9. 信号与槽:在需要线程间通信时,可以通过信号与槽机制进行数据传递和同步。

总结

在使用 QThread 时,正确停止线程的执行非常重要,以避免 QThread: Destroyed while thread is still running 错误。我们通过发送停止信号并等待线程停止的方式,解决了这个问题。这样,我们可以确保在线程执行完毕后再销毁 QThread 对象,以保持线程的正确生命周期。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。