目录
做应用软件,因为主界面的原因,业务线程可以说极其重要。如果不开线程的话,软件很难做到流畅,一个卡顿的软件很难被用户买单。
很多初学者会被线程的同步机制给吓到,或者觉得加锁会影响性能,所以干脆会拒绝对线程的使用。
但是合理和高效的使用线程,以及对多线程的掌握程度,往往又决定了软件的性能以及面试的结果和定级。
因为单纯就应用软件方向而言,如果面试者对线程很陌生,面试官其实也基本可以判定,面试者日常中所负责的只是基础的增删改查业务,而不具备项目的整体把控能力。
为什么说要掌握线程,以及线程的同步机制呢。因为很多处理过程,都是耗时的操作,如果都让主线程来处理,软件主界面会不堪重负。
比如用户点击了一个生成报表按钮,如果数据量很大,不是几秒钟就能做好的,这时候用户又点击了另外一个设置按钮,然后设置界面等了1-2秒才能弹出来,会极大降低体验。
如果在设计之初,就把可能的耗时任务都放到不同的子线程去处理,处理完毕后,再通过回调或者信号机制通知主线程,就不会影响到主界面,所以搭建一个稳定、合理的线程池是一件有必要去做的事情。
如果同步机制做不好,可能就会出现资源抢占、死锁等问题,造成软件卡死。所以说为了开发出稳定的软件和功能,更需要掌握线程,以及多线程的同步机制。
幸运的是,很多框架都已经搭建好了现有的线程类或者线程池,如果使用框架,就不需要我们自己去写原生的多线程,提高了开发效率。
下面一起了解一下如何在qt中,创建子线程处理耗时操作。
1、QThread
QThread类提供了一种独立于平台的方式来管理线程。一个QThread对象管理一个线程。
子线程在run()函数中开始执行,通过start()启动线程,通过quit()和wait()来退出线程。
2、何时使用线程
当需要处理比较耗时的操作,不希望影响到主界面的操作时,就可以考虑使用线程,来承接那部分耗时的任务,
比如我们在布局上显示4个视频,如果在主线程渲染,那么主界面马上就吃不消了,用户点击操作界面将会十分卡顿。
3、不使用多线程会有什么现象
下面我在主线程模拟了一段耗时的代码,以模拟视频渲染,当点击开始的按钮后,进入耗时操作:
void Form::on_pushButton_clicked()
{
//模拟解码耗时操作
int cnt=0;
while (cnt<50) {
cnt++;
qDebug()<<"run main"<<"cnt="<<cnt;
QThread::msleep(300);
}
}
界面现象:
这时界面卡主,用户虽然可以操作,但是很卡顿,甚至软件有时候会直接卡死,就需要等耗时循环结束,界面才恢复正常。
但是这往往不是我们希望的效果,于是请出线程,来帮助处理,分担压力。
4、使用线程处理耗时操作
#ifndef WORK_H
#define WORK_H
#include <QObject>
#include <QThread>
#include <QDebug>
class Work:public QThread
{
Q_OBJECT
public:
Work():_stop(true)
{
}
~Work(){
qDebug()<<"thread start quit";
_stop = false;
QThread::quit();
QThread::wait();
qDebug()<<"thread success!";
}
void run() override
{
//模拟解码耗时操作
int cnt=0;
while (cnt<50 && _stop) {
cnt++;
qDebug()<<"run Thread"<<"cnt="<<cnt;
QThread::msleep(300);
}
}
private:
bool _stop;
};
#endif // WORK_H
work类继承自QThread,重写父类的QThread的run方法,在run方法中,延时300毫秒,模拟耗时操作。
在btn中启动线程:
void Form::on_pushButton_clicked()
{
//模拟解码耗时操作
// int cnt=0;
// while (cnt<50) {
// cnt++;
// qDebug()<<"run main"<<"cnt="<<cnt;
// QThread::msleep(300);
// }
if(_w)
_w->start();
}
通过调用start()函数,启动子线程,即会调用run方法,进行处理。
界面效果正常:
5、线程退出注意点
使用以下,先quit,再wait。
QThread::quit();
QThread::wait();
因为quit()函数虽然是用来停止QThread的,但是由于Qt本身拥有事件循环机制,所以在调用完quit()后,QThread可能还没有停止。
所以在执行quit()后,通常调用wait()来等待QThread子线程的结束,即run函数的结束,这样才能保证在释放QThread的时候,它的子线程是完全停止运行的状态。