QT GUI冻结,即使我运行在单独的线程

QT GUI freezes even though Im running in separate thread

本文关键字:运行 单独 线程 GUI 冻结 QT      更新时间:2023-10-16

我有一个小的聊天应用程序,我使用SQLite数据库来存储所有的对话。我注意到应用程序会随机死机,然后我必须将其最小化或最大化以使其再次运行。我认为问题可能是SQLite选择/插入导致gui冻结。我决定尝试将所有SQLite方法移动到一个单独的线程中。

这样做后,应用程序仍然冻结。

一些可能值得知道的事情:

  1. 我直接在我的MainWindow中使用QTcpSocket,但似乎在单独的线程中运行QTcpSocket没有用处?

  2. 我已经把SQLite方法分离成一个新的线程(见下面的实现)

  3. 我使用3 WebViews来显示我的聊天消息,整个应用程序GUI是用这些WebViews

  4. 构建的

我下面的代码真的在一个单独的线程中运行吗?GUI仍然会死机。

My header file:

class dbThread : public QObject
{
     Q_OBJECT
public:
     dbThread(QObject* parent);
public slots:
     bool openDB(QString agentID);
signals:
     void clearPreviousHistory();
private:
     QSqlDatabase db;
     QHash<QString, QString> countries;
};

My cpp file:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);
dbtrad->openDB(userID);
connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();
}

dbThread::dbThread(QObject * parent): QObject(parent) {
}
bool dbThread::openDB(QString agentID) {
    qDebug() << "OPEN DB FROM THREAD ";
    // Find QSLite driver
    db = QSqlDatabase::addDatabase("QSQLITE");
    // ......
}

这是我如何从我的MainWindow调用dbThread方法:

dbtrad->getHistory(channelId);

编辑

新代码:

// Start database thread
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);
connect(this, SIGNAL(requestOpenDB(QString)), dbtrad, SLOT(openDB(QString)));
thread->start();
emit requestOpenDB(userID);

dbtrad->openDB(userID);将像任何正常函数一样执行(为什么要这样?),在GUI线程

moveToThread允许在一个单独的线程中使用信号执行插槽。

如果你想在线程中执行openDB,你可以使用

触发它的执行
 connect (thread, SIGNAL(started()), dbtrad, SLOT(openDBWithUIDAlreadySet()))

 connect (this, SIGNAL(requestOpenDB(int)), dbtrad, SLOT(openDB(int)))

您需要使用现有的或额外的信号。Qthread::start()发出started()信号。你也可以定义

MainWindow{
signals:
    void  requestOpenDB(int);
    void queryHistory(int channelid);
}

并使用

手动发出信号
emit requestOpenDB(userID);    //for openDB
emit queryHistory(channelId);  // for getHistory

来自dbThread对象的响应也需要使用连接到插槽的信号来给出。像一个通知。

  1. QTcpSocket确实不需要在一个单独的线程中

  2. 只要所有的数据库访问都是在创建数据库的线程中完成的,这也应该是没有问题的

  3. 现在到有趣的部分:我认为你在主线程中创建数据库…通过调用dbtrad->openDB(userId)

是的,所以qt moveToThread()不做你期望它做的事情。你在主线程中调用的函数只会在主线程中执行。数据库访问导致GUI冻结。

moveToThread只在一个单独的线程中移动"事件处理"。这意味着使用Qt::QueuedConnection连接的dbThread的任何插槽将在新线程中执行。

以下方法将只在你的主ui线程中执行getHistory()方法。您需要在主线程中创建一个信号,并使getHistory()成为dbThread类的插槽。然后将两者连接起来。

阅读文档和日志是必要的!!
在日志中你有一个警告,如果对象有父线程,你不能移动到线程。文档也清楚地说:

更改此对象及其子对象的线程关联。的对象如果有父对象,则不能移动。事件处理将


正确的修复方法:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    thread = new QThread(this);
    dbtrad = new dbThread(); // NO PARENT
    dbtrad->moveToThread(thread);
    // run object method in thread assigned to this object:
    QMetaObject::invokeMethod(dbtrad, "openDB", Qt::QueuedConnection, Q_ARG(QString, userID));
    connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
    thread->start();
}
MainWindow::~MainWindow()
{
    dbtrad->deleteLater();
    thread->quit();
    thread->wait(5000); // wait max 5 seconds to terminate thread
}