❤️作者主页:凉开水白菜
❤️作者简介:共同学习,互相监督,热于分享,多加讨论,一起进步!
❤️专栏目录:【零基础学QT】文章导航篇
❤️专栏资料:https://pan.baidu.com/s/192A28BTIYFHmixRcQwmaHw 提取码:qtqt
❤️点赞 👍 收藏 ⭐再看,养成习惯
订阅的粉丝可通过PC端文末加我微信,可对文章的内容进行一对一答疑!
简介
在进行桌面应用程序开发的时候, 假设应用程序在某些情况下需要处理比较复杂的逻辑, 如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其他线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率。
在 qt 中使用了多线程,有些事项是需要额外注意的:
默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新
子线程负责后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理
主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号槽机制
QThread的两种使用方法
继承QThread
使用继承的方法我们需要新建一个类
先继承Object后面修改代码
然后要在QT中使用我们的多线程我们还需要重写run函数,将我们的任务函数放到run函数中执行,为了区分线程我们再给我们的线程创建一个name和一个开启和暂停标志,完整代码如下;
#ifndef WORKTHREAD_H
#define WORKTHREAD_H
#include <QThread>
#include <QDebug>
class WorkThread : public QThread
{
Q_OBJECT
public:
explicit WorkThread(QObject *parent = nullptr);
WorkThread(QString thread_name, QObject *parent = nullptr); // 该构造函数可以设置线程名字
void stop();
private:
void run();
private:
QString thread_name;
bool thread_state;
signals:
};
#endif // WORKTHREAD_H
#include "workthread.h"
WorkThread::WorkThread(QObject *parent) : QThread(parent),
thread_name(QString((int)QThread::currentThread()))
{
thread_state = false;
}
WorkThread::WorkThread(QString thread_name, QObject *parent)
{
Q_UNUSED(parent)
if(thread_name != "")
this->thread_name = thread_name;
else{
this->thread_name = QString((int)QThread::currentThreadId());
}
thread_state = false;
}
void WorkThread::stop()
{
thread_state = false;
}
void WorkThread::run()
{
thread_state = true;
while(thread_state)
{
static int count = 0;
qDebug() << thread_name << QString("[%1]").arg(count++)<< ":runing" << endl;
QThread::sleep(1);
}
}
这也相当于是一个模板,如果是使用继承的QThread这个类来实现多线程可以直接创建一个Class类直接复制上面的内容到里面就可以使用,主类中我们这样调用;
WorkThread *worker1 = new WorkThread("线程1");
worker1->start();
WorkThread *worker2 = new WorkThread("线程2");
worker2->start();
WorkThread *worker3 = new WorkThread("线程3");
worker3->start();
然后效果如下图
moveToThread
使用这个方法同样我们需要新建一个类,这次我们直接继承Object就可以了
然后在该类中我们正常编写我们的函数,为了体现任务处理需要时间的情况我们还是使用QThread::sleep();来实现效果演示,我们先可以不适用moveToThread;
我们在界面上创建一个按钮并绑定按下信号这个函数上,然后我们可以看到界面卡住好几秒都不能进行其他操作
下面是使用moveToThread的方法
thread_object = new WorkObject;
QThread *thread = new QThread;
thread_object->moveToThread(thread);
thread->start();
connect(ui->pushButton, &QPushButton::clicked, thread_object, &WorkObject::StartWork);
这个方法比前面的方法更加灵活一些,最终使用的选择都是依照具体情况来使用;
互斥锁
上面的实验我们都是打印一个静态变量count的值,从打印信息来看他的顺序是非常乱的,也就是说我们的三个线程都在抢占这个变量值的使用权限,这里我们就引入了互斥锁的概念,他的作用就是同一个物品只能同时容纳一个人来使用,在程序中就是一个变量只能一个线程使用,其他线程需要等待这个线程使用完才能使用,那这里可以猜想一下我们添加了互斥锁后这里打印是什么效果;
QT的互斥锁使用非常简单,我们只需要定义一个QMutex,然后使用他的lock和unlock就可以实现上锁和开锁;
void WorkThread::run()
{
static QMutex mutex;
thread_state = true;
while(thread_state)
{
static int count = 0;
mutex.lock();
qDebug() << thread_name << QString("[%1]").arg(count++)<< ":runing" << endl;
mutex.unlock();
QThread::sleep(1);
}
}
添加锁后我们的打印信息就来到了下面这样
这里的逻辑就是,这个count的值在一个线程中已经在被操作了那其他线程只能等待这边打印完成过后才能继续去count++,所以他的值打印出来只能是顺序的,这里需要注意的是我们使用锁一定要成对出现;