Qt執行耗時操作導致界面卡頓的三種解決辦法

1.問題描述

Qt界面程序開發中,會遇到執行耗時操作時,導致界面卡頓。原因是界面主線程是單線程,如果在UI主線程中執行耗時操作,例如點擊按鈕,響應函數去數據庫查詢數據,數據量比較大時,查詢需要幾秒鐘甚至幾十秒的時間,如果UI主線程一直等待響應函數返回,阻塞在響應函數內部,就無法響應界面的其他消息或者事件,界面就會卡死,無響應;

 

2.解決方法

2.1用Qt::QueuedConnection去連接信號槽

Qt::QueuedConnection是用隊列的形式執行操作,點擊pushButtonStart按鈕之後,會立刻返回,不用等到startSmartApart();執行完成再返回;startSmartApart();5秒內執行完,可以考慮這種方法,超過5秒,界面也會卡頓;

connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() {
		//QtConcurrent::run(this, &test::startSmartApart);
		startSmartApart();
	},Qt::QueuedConnection);

2.2用QtConcurrent::run將類函數放入線程中執行;

(1)QtConcurrent::run會創建一個新的線程,將類函數放入新的線程中執行;也可以返回返回值;

connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() {
        QtConcurrent::run(this, &test::startSmartApart);
        //startSmartApart();
    },Qt::QueuedConnection);

(2)可以給執行的類函數startSmartApartCount(int count)傳入多個參數,最多5個參數;

connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() {
        //QtConcurrent::run(this, &test::startSmartApart);
        int retval = -1;
        QtConcurrent::run(this, &test::startSmartApartCount,100);
        //startSmartApart();
    },Qt::QueuedConnection);

(3)同步方法獲取函數的返回值,但是 future.waitForFinished();會阻塞主線程等待結果返回,導致界面卡死;

connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() { 
        int retval = -1;
        QFuture<int> future = QtConcurrent::run(this, &test::startSmartApartCount, 10);
        future.waitForFinished();//阻塞等待結果返回
        qDebug() << future.result();
    },Qt::QueuedConnection);

(4)異步方法獲取函數返回值,使用QFutureWatcher信號槽監視返回結果,不阻塞主線程,界面很流暢

//類的成員變量

QFuture<int> future;

QFutureWatcher<int>* watcher;

watcher = new QFutureWatcher<int>();
    connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() {
         future = QtConcurrent::run(this, &test::startSmartApartCount, 2);
        
        QObject::connect(watcher, &QFutureWatcher<int>::finished,this, [&]() {
            qDebug() << future.result();
        });
        watcher->setFuture(future);
    },Qt::QueuedConnection);

2.3用QThread執行耗時操作

(1)創建線程類

頭文件

#ifndef SmartApartPlayThread_H
#define SmartApartPlayThread_H

#include <QThread>
class SmartApartPlayThread : public QThread
{
    Q_OBJECT

public:
    SmartApartPlayThread();
    ~SmartApartPlayThread();
    int StartSmartApartPlay(void* pmulti,int index);
    void run();
signals:
    void signalStartApartPlay(int index);

private:
    void* m_pmulti=nullptr;
    int m_index=0;
};

#endif // COPYFILETHREAD_H

 

源文件

#include "SmartApartPlayThread.h"
#include <windows.h>
SmartApartPlayThread::SmartApartPlayThread()
{

}

SmartApartPlayThread::~SmartApartPlayThread()
{

}

int SmartApartPlayThread::StartSmartApartPlay(void* pmulti, int index)
{
    m_pmulti = pmulti;
    m_index = index;
    if (m_pmulti!=nullptr)
    {
        start();
    }
    return 0;
}



void SmartApartPlayThread::run()
{
    
    while (1)
    {
        Sleep(10000);
    }
}

(2)按鈕響應函數中啓動線程執行耗時操作

connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() {
        m_SmartApartPlayThread.StartSmartApartPlay(this,10);
    },Qt::QueuedConnection);
    

 

3.總結

要實現好的用戶體驗,實現流程的界面操作程序,就不能老是卡,比較好的方式就是採用異步操作、多線程等方法來實現異步,併發;這樣纔會有好的交互體驗;QtConcurrent::run和線程其實原理上都是多線程併發方式,只是QtConcurrent::run封裝了線程的操作;可以執行類函數,更方便一些;

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章