1.问题描述
Qt界面程序开发中,会遇到执行耗时操作时,导致界面卡顿。原因是界面主线程是单线程,如果在UI主线程中执行耗时操作,例如点击按钮,响应函数去数据库查询数据,数据量比较大时,查询需要几秒钟甚至几十秒的时间,如果UI主线程一直等待响应函数返回,阻塞在响应函数内部,就无法响应界面的其他消息或者事件,界面就会卡死,无响应;
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)用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);
(3)用QThread执行耗时操作
创建线程类
头文件
#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;
};
源文件
#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);
}
}
使用线程类
//按钮响应函数中启动线程执行耗时操作
connect(ui.pushButtonStart, &QPushButton::clicked, this, [=]() {
m_SmartApartPlayThread.StartSmartApartPlay(this,10);
},Qt::QueuedConnection);
3.总结
要实现好的用户体验,实现流程的界面操作程序,就不能老是卡,比较好的方式就是采用异步操作、多线程等方法来实现异步,并发;这样才会有好的交互体验;QtConcurrent::run和线程其实原理上都是多线程并发方式,只是QtConcurrent::run封装了线程的操作;可以执行类函数,更方便一些;