Qt多线程的使用记录
为什么要使用多线程?
在运行桌面运行程序时,如果在主进程中存在大量数据需要进行处理,此时移动UI界面,则会出现**(未响应)**的情况。
为了解决这种情况带来的程序卡顿问题,可以通过使用其他线程把占据时间长的程序中的任务放到后台去处理。这样即可解决程序卡顿,提升程序的执行效率。
1.Qt实现多线程
1.QThread的两种方式
方式一 新建一个线程类继承QThread
Qt中最基础的线程创建方式是使用QThread新建一个线程类继承QThread,重写
run()
函数并通过start()
函数启动线程。方式二 使用
moveToThread
这种方式是把一个继承于QObject的类转移到一个Thread里
任务:这里分别用两种方式,通过子线程去刷新主线程的进度条。
2.方式一
打开Qt新建一个工程Thread_01
选择QWidget这个基类,并修改为类名为 MainWidget。
双击MainWidget.ui文件,并拖拽合适的控件。
鼠标点击Display Widgets栏下的Progress Bar,拖拽进MainWidget里,并修改起始参数为0。
鼠标点击Button栏下的Push Button,拖拽进MainWidget里,并修改控件名为start,修改按键名为 开始 。
鼠标点击Spacer的Horizontal Spacer拖拽进MainWidget里 。
框选Horizontal Spacer和start三项,并点击水平布局。
鼠标点击Spacer的Vertical Spacer拖拽进MainWidget里 。
鼠标点击MainWidget,并点击垂直布局。
鼠标点击MainWidget,并设置参数。
至此,UI界面设计完成。
鼠标点击编辑
鼠标点击工程Thread_01,并右击选择 Add New…
新建一个继承于QObject的类,MyThread。
打开MyThread.h 修改这两处
修改后
打开MyThread.cpp 修改这处QObject为QThread
打开MyThread.h 添加线程的run()
函数。
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
protected:
//QThread的虚函数
//线程处理函数
//不能直接调用,通过start()函数间接调用(在主线程中)
void run();
signals:
};
#endif // MYTHREAD_H
在MyThread.cpp重写run()
函数。
#include "mythread.h"
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
}
分析:想要鼠标点击开始按钮时,进度条开始读数。
- 首先,鼠标按下时需要给子线程一个信号让其开始计数,因此需要添加一个公共变量
bool m_Start
,用于主线程调用时,控制计数开始。- 其次,当子线程计数时要逐渐更新进度信号发送给主线程,再由主线程传递给UI界面的进度条控件(需注意,子线程不可直接控制UI界面的控件!!!)。
- 在子进程结束时,需要返回进程结束信号,并在主进程中,将其析构。
代码:
mainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWidget; }
QT_END_NAMESPACE
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = nullptr);
~MainWidget();
private:
Ui::MainWidget *ui;
};
#endif // MAINWIDGET_H
mainWidget.cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include "mythread.h"
#include <QDebug>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
qDebug() << "主线程的线程地址: " << QThread::currentThread();
//1.创建子线程对象
MyThread *m_thread = new MyThread;
//2.启动子线程并设置m_Start为true
connect(ui->start,&QPushButton::clicked,this,[=]()
{
m_thread->m_Start = true;
m_thread->start();
});
//3.将子线程发送过来的信号传递给ui界面的进度条
connect(m_thread,&MyThread::updateProgress,this,[=](int nNum)
{
ui->progressBar->setValue(nNum);
});
//4.子线程完成时将其析构
connect(m_thread,&MyThread::isDone,this,[=]()
{
m_thread->m_Start = false; //停止计数
m_thread->quit(); //停止线程
m_thread->wait();//等待线程处理完线程手头工作
});
//当按窗口右上角X时,窗口触发destroyed()
connect(this,&MainWidget::destroyed,this,[=]()
{
m_thread->m_Start = false; //停止计数
m_thread->quit(); //停止线程
m_thread->wait();//等待线程处理完线程手头工作
});
}
MainWidget::~MainWidget()
{
delete ui;
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
bool m_Start;
protected:
void run();
//QThread的虚函数
//线程处理函数
//不能直接调用,通过start()函数间接调用(在主线程中)
signals:
void isDone(); //进程结束信号
void updateProgress(const int nNum); //进度更新信号
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QElapsedTimer>
#include <QDebug>
MyThread::MyThread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
qDebug() << "子线程的线程地址: " << QThread::currentThread();
QElapsedTimer time;
time.start();
int i = 0;
while(m_Start)
{
i++;
if(i>100)
{
int ms = time.elapsed();
qDebug() << "用时" << ms << "毫秒";
emit isDone();
//结束进度读取结束,发射进程结束信号
}
emit updateProgress(i); //更新进度读数
msleep(100);
}
}
演示结果动图展示
3.方式二
新建Qt项目Thread_02并绘制好ui界面
给项目添加新的类 MyWork
分析:想要鼠标点击开始按钮时,进度条开始读数。
- 首先,鼠标按下时需要给子线程一个信号让其开始计数,因此需要添加一个
bool m_Start
,用于主线程发送信号时work函数开始工作。- 其次,当子线程计数时要逐渐更新进度信号发送给主线程,再由主线程传递给UI界面的进度条控件(需注意,子线程不可直接控制UI界面的控件!!!)。
- 在子进程结束时,需要返回进程结束信号,并在主进程中,将其析构
点击mywork.h添加work()
函数及相关信号。
点击mywork.cpp添加 work()
函数的内容。
点击MainWidget.h添加isRun()
信号
代码:
mainWidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWidget; }
QT_END_NAMESPACE
class MainWidget : public QWidget
{
Q_OBJECT
public:
MainWidget(QWidget *parent = nullptr);
~MainWidget();
private:
Ui::MainWidget *ui;
signals:
void isRun(bool start);
};
#endif // MAINWIDGET_H
mainWidget.cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include "mywork.h"
#include <QThread>
#include <QDebug>
MainWidget::MainWidget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::MainWidget)
{
ui->setupUi(this);
qDebug() << "主线程的线程地址: " << QThread::currentThread();
//1.创建任务类对象
MyWork *m_work = new MyWork;
//2.创建子线程对象
QThread *t1 = new QThread;
//3.将任务类对象移入子线程
m_work->moveToThread(t1);
connect(this,&MainWidget::isRun,m_work,&MyWork::work);
//4.启动任务和子线程并设置m_Start为true
connect(ui->start,&QPushButton::clicked,this,[=]()
{
emit isRun(true);
t1->start();
});
//5.将任务对象发送过来的信号传递给ui界面的进度条
connect(m_work,&MyWork::updateProgress,this,[=](int nNum)
{
ui->progressBar->setValue(nNum);
});
//6.任务对象完成时将其所在线程析构
connect(m_work,&MyWork::isDone,this,[=]()
{
t1->quit(); //停止线程
t1->wait();//等待线程处理完线程手头工作
});
//当按窗口右上角X时,窗口触发destroyed()
connect(this,&MainWidget::destroyed,this,[=]()
{
t1->quit(); //停止线程
t1->wait();//等待线程处理完线程手头工作
});
}
MainWidget::~MainWidget()
{
delete ui;
}
mywork.h
#ifndef MYWORK_H
#define MYWORK_H
#include <QObject>
class MyWork : public QObject
{
Q_OBJECT
public:
explicit MyWork(QObject *parent = nullptr);
void work(bool m_Start); //work函数用于执行进度更新
signals:
void isDone(); //进程结束信号
void updateProgress(const int nNum); //进度更新信号
};
#endif // MYWORK_H
mywork.cpp
#include "mywork.h"
#include <QElapsedTimer>
#include <QDebug>
#include <QThread>
MyWork::MyWork(QObject *parent) : QObject(parent)
{
}
void MyWork::work(bool m_Start)
{
qDebug() << "子线程的线程地址: " << QThread::currentThread();
QElapsedTimer time;
time.start();
int i = 0;
while(m_Start)
{
i++;
if(i>100)
{
int ms = time.elapsed();
qDebug() << "用时" << ms << "毫秒";
m_Start=false;
//结束进度读取结束,发射进程结束信号
}
emit updateProgress(i); //更新进度读数
QThread::msleep(100);
}
}
演示结果动图展示
关注公众号后台回复:Qt多线程 | 资源下载
下载工程文件
(为了防止错误,请直接复制粗体字到后台回复)