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

时间:2014-05-22 07:45:27

标签: c++ qt

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

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

可能值得了解的一些事情:

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

  2. 我已将SQLite方法分成新线程(请参阅下面的实现)

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

  4. 下面的代码是否真的在一个单独的线程中运行? GUI仍然冻结。

    我的标题文件:

    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;
    };
    

    我的cpp文件:

    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");
    
        // ......
    }
    

    这就是我从dbThread

    调用MainWindow方法的方法
    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);
    

4 个答案:

答案 0 :(得分:2)

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 :(得分:0)

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

  2. 只要从创建数据库的那个线程完成所有数据库访问,它也应该没问题

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

答案 2 :(得分:0)

是的,所以qt moveToThread()没有做你期望它做的事情。您从主线程调用的函数将仅在主线程中执行。该数据库访问导致GUI死机。

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

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

答案 3 :(得分:0)

阅读文档和日志至关重要!!!
在日志中,您有一个警告,如果对象有父母,您可以移动到线程。 另外documentation clearly says that

  

更改此对象及其子对象的线程关联。的的   如果对象具有父,则无法移动该对象。事件处理将   继续在targetThread。

<小时/> 正确的解决方法:

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
}