我们在进行桌面应用程序开发的时候,如果应用程序在某些情况下需要处理一些比较复杂的逻辑,如果只有一个线程就会导致界面卡顿。此时我们就需要使用多线程,其中一个处理窗口事假,另外一个处理其他逻辑,多个现场同时运行,这样可以大大提高程序的运行效率以及用户体验。
我们在Qt里使用多线程的时候,是有几点需要注意的:
- Qt里默认的线程称之为窗口线程,也就是主线程,主要负责窗口事件的处理以及窗口控件的数据更新。
- 子线程负责后台业务逻辑处理,不负责任何和窗口对象有关的操作,和窗口有关操作一律交给主线程处理。
- 主线程和子线程之间的数据传递需要用到Qt里的信号与信号槽机制
1.QThread线程类
Qt里提供了一个线程类,通过这个线程我们可以创建子线程。
1.1常用API函数
// QThread 类常用 API
// 构造函数
QThread::QThread(QObject *parent = Q_NULLPTR);
// 判断线程中的任务是不是处理完毕了
bool QThread::isFinished() const;
// 判断子线程是不是在执行任务
bool QThread::isRunning() const;
// Qt中的线程可以设置优先级
// 得到当前线程的优先级
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);
优先级:
QThread::IdlePriority // 最低的优先级
QThread::LowestPriority
QThread::LowPriority
QThread::NormalPriority
QThread::HighPriority
QThread::HighestPriority
QThread::TimeCriticalPriority // 最高的优先级
QThread::InheritPriority //子线程和其父线程的优先级相同, 默认是这个
// 退出线程, 停止底层的事件循环
// 退出线程的工作函数
void QThread::exit(int returnCode = 0);
// 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
// 等待任务完成, 然后退出线程, 一般情况下会在 exit() 后边调用这个函数
bool QThread::wait(unsigned long time = ULONG_MAX);
1.2信号槽
//和调用exit()效果相同 调用此函数之后在调用wait()函数 void QThread::quit(); //启动子线程 void QThread::start(Priority priority = InheritPriority); //线程退出,可能会马上终止线程,一帮情况下不使用此函数 void QThread::terminate(); //线程中执行的任务结束之后释放此信号 void QThread::finished(); //开始工作前释放此信号,一般不使用 void QThread::started();
1.3静态函数
//返回一个指向管理当前执行线程的QThread的指针 QThread* QThread::currentThread(); //返回可以在系统上运行的理想线程数 (与电脑CPU核心数相同) int QThread::ideaThreadCount(); //线程休眠函数 void QThread::msleep(unsigned long msecs);//毫秒 void QThread::sleep(unsigned long secs);//秒 void QThread::usleep(unsigned long usecs);//微秒
1.4任务处理函数
[virtual protected]void QThread::run();
run是一个虚函数,如果想要创建的子线程执行某个任务,需要写一个类继承QThread,在子类里重写run()方法,函数内实现对应具体的处理流程。此函数是protected的,不能在类外部调用,需要当前线程对象调用start()启动子线程,这个函数在线程内部就被调用了。
2.使用方式1
2.1操作步骤
第一种操作方式就是创建QThread的一个子类。它的特点就是简单。
1.创建线程类子类
class MyThread:public QThread{
...
}2.重写父类run方法
class MyThread:public QThread{
protected:
void run(){
...
}
}3.主线程里创建子线程对象
MyThread*subThread = new MyThread;
4.启动子线程
subThread->start();
子线程创建之后与主线程之间可以通过信号与槽机制进行通信,但是切记子线程中不可操作程序里的窗口类对象,只有主线程可以,否则程序会崩溃。
2.2示例代码
假设我们现在要写一个生成随机数并且用两种不同方式将他们排序的程序,我们就可以将生成以及两种排序写成三个子线程。

//mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include <QObject>
#include<QVector>
class Generate : public QThread
{
Q_OBJECT
public:
explicit Generate(QObject *parent = nullptr);
void recvNum(int num);
protected:
void run()override;
signals:
void sendArray(QVector<int>num);
private:
int m_num;
};
class BubbleSort : public QThread
{
Q_OBJECT
public:
explicit BubbleSort(QObject *parent = nullptr);
void recvArray(QVector<int>list);
protected:
void run()override;
signals:
void finish(QVector<int>num);
private:
QVector<int>m_list;
};
class QuickSort : public QThread
{
Q_OBJECT
public:
explicit QuickSort(QObject *parent = nullptr);
void recvArray(QVector<int>list);
protected:
void run()override;
signals:
void finish(QVector<int>num);
private:
QVector<int>m_list;
};
#endif // MYTHREAD_H
//mythread.cpp
#include "mythread.h"
#include<QElapsedTimer>
#include<QDebug>
Generate::Generate(QObject *parent)
: QThread{parent}
{
}
void Generate::recvNum(int num)
{
m_num=num;
}
void Generate::run()
{ qDebug()<<"生成随机数的线程地址"<<QThread::currentThread();
QVector<int>list;
QElapsedTimer time;
time.start();
for(int i=0;i<m_num;++i){
list.push_back(rand()%100000);
}
int milsec=time.elapsed();//执行程序所需要的时间
qDebug()<<"生成"<<m_num<<"个随机数"<<"总共用时:"<<milsec<<"毫秒";
emit sendArray(list);
}
BubbleSort::BubbleSort(QObject *parent):QThread(parent)
{
}
void BubbleSort::recvArray(QVector<int> list)
{
m_list=list;
}
void BubbleSort::run()
{ qDebug()<<"冒泡排序的线程地址"<<QThread::currentThread();
QVector<int>list;
QElapsedTimer time;
time.start();
int temp;
for(int i=0;i<m_list.size();++i){
for(int j=0;j<m_list.size()-i-1;++j){
if(m_list[j]>m_list[j+1]){
temp =m_list[j];
m_list[j]=m_list[j+1];
m_list[j+1]=temp;
}
}
}
int milsec=time.elapsed();//排序所要时间
qDebug()<<"完成冒泡排序用时:"<<milsec<<"毫秒";
emit finish(m_list);
}
QuickSort::QuickSort(QObject *parent)
{
}
void QuickSort::recvArray(QVector<int> list)
{
m_list=list;
}
void QuickSort::run()
{
qDebug()<<"快速排序的线程地址"<<QThread::currentThread();
QVector<int>list;
QElapsedTimer time;
time.start();
std::sort(m_list.begin(),m_list.end());
int milsec=time.elapsed();//排序所要时间
qDebug()<<"完成快速排序用时:"<<milsec<<"毫秒";
emit finish(m_list);
}
//mainwindow.h
ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"mythread.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void starting(int num);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建子线程对象
Generate*gen=new Generate;
BubbleSort*bubble=new BubbleSort;
QuickSort*quick=new QuickSort;
//启动子线程
connect(this,&MainWindow::starting,gen,&Generate::recvNum);
connect(ui->start,&QPushButton::clicked,this,[=](){
emit starting(10000);
gen->start();
});
//排序线程接收生成数据
connect(gen,&Generate::sendArray,bubble,&BubbleSort::recvArray);
connect(gen,&Generate::sendArray,quick,&QuickSort::recvArray);
//接收生成随机数子线程数据
connect(gen,&Generate::sendArray,this,[=](QVector<int>list){
//排序线程启动
bubble->start();
quick->start();
for(int i=0;i<list.size();++i){
ui->randlist->addItem(QString::number(list.at(i)));
}
});
//接收排序后有序序列
connect(bubble,&BubbleSort::finish,this,[=](QVector<int>list){
for(int i=0;i<list.size();++i){
ui->bubblelist->addItem(QString::number(list.at(i)));
}
});
connect(quick,&QuickSort::finish,this,[=](QVector<int>list){
for(int i=0;i<list.size();++i){
ui->quicklist->addItem(QString::number(list.at(i)));
}
});
//回收线程资源
connect(this,&MainWindow::destroyed,this,[=](){
gen->quit();
gen->wait();
gen->deleteLater();
bubble->quit();
bubble->wait();
bubble->deleteLater();
quick->quit();
quick->wait();
quick->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
3.使用方式2
第二种方式更加灵活但是也有点复杂
3.1操作步骤
1.创建一个基于QObject的类
class MyWork:public QThread{
...
}2.添加一个公共成员函数,函数体就是我们要执行的任务
class MyWork:public QThread
{
public:
void working();
}3.主线程里创建子线程对象
QThread*sub=new QThread;
4.在主线程里创建工作类对象(千万不要指定父对象!!!)
MyWork*work=new MyWork(this);//错误写法❌️ MyWork*work=new MyWork;//正确写法
5.将工作类对象移动到子线程里
work->moveToThread(sub);
6.启动子线程,调用start();但是此时工作类对象并没有开始工作。
7.MyWork类对象调用工作函数,在子线程中进行工作。
3.2实例代码
还是以排序为例,我们来看第二种方法如何实现
//mythread.h
ifndef MYTHREAD_H
#define MYTHREAD_H
#include<QThread>
#include <QObject>
#include<QVector>
class Generate : public QObject
{
Q_OBJECT
public:
explicit Generate(QObject *parent = nullptr);
void working(int num);
signals:
void sendArray(QVector<int>num);
};
class BubbleSort : public QObject
{
Q_OBJECT
public:
explicit BubbleSort(QObject *parent = nullptr);
void working(QVector<int>list);
signals:
void finish(QVector<int>num);
};
class QuickSort : public QObject
{
Q_OBJECT
public:
explicit QuickSort(QObject *parent = nullptr);
void working(QVector<int>list);
signals:
void finish(QVector<int>num);
};
#endif // MYTHREAD_H
//mythread.cpp
#include "mythread.h"
#include<QElapsedTimer>
#include<QDebug>
#include<QThread>
Generate::Generate(QObject *parent)
: QObject{parent}
{
}
void Generate::working(int num)
{ qDebug()<<"生成随机数的线程地址"<<QThread::currentThread();
QVector<int>list;
QElapsedTimer time;
time.start();
for(int i=0;i<num;++i){
list.push_back(rand()%100000);
}
int milsec=time.elapsed();//执行程序所需要的时间
qDebug()<<"生成"<<num<<"个随机数"<<"总共用时:"<<milsec<<"毫秒";
emit sendArray(list);
}
BubbleSort::BubbleSort(QObject *parent):QObject(parent)
{
}
void BubbleSort::working(QVector<int>list)
{ qDebug()<<"冒泡排序的线程地址"<<QThread::currentThread();
QElapsedTimer time;
time.start();
int temp;
for(int i=0;i<list.size();++i){
for(int j=0;j<list.size()-i-1;++j){
if(list[j]>list[j+1]){
temp =list[j];
list[j]=list[j+1];
list[j+1]=temp;
}
}
}
int milsec=time.elapsed();//排序所要时间
qDebug()<<"完成冒泡排序用时:"<<milsec<<"毫秒";
emit finish(list);
}
QuickSort::QuickSort(QObject *parent):QObject(parent)
{
}
void QuickSort::working(QVector<int>list)
{
qDebug()<<"快速排序的线程地址"<<QThread::currentThread();
QElapsedTimer time;
time.start();
std::sort(list.begin(),list.end());
int milsec=time.elapsed();//排序所要时间
qDebug()<<"完成快速排序用时:"<<milsec<<"毫秒";
emit finish(list);
}
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include"mythread.h"
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals:
void starting(int num);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include<QThread>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//创建子线程对象
QThread*t1=new QThread;
QThread*t2=new QThread;
QThread*t3=new QThread;
//任务类实例对象
Generate*gen=new Generate;
BubbleSort*bubble=new BubbleSort;
QuickSort*quick=new QuickSort;
//将任务对象移动到某个子线程
gen->moveToThread(t1);
bubble->moveToThread(t2);
quick->moveToThread(t3);
//启动子线程
connect(this,&MainWindow::starting,gen,&Generate::working);
connect(ui->start,&QPushButton::clicked,this,[=](){
emit starting(10000);
t1->start();
});
//排序线程接收生成数据
connect(gen,&Generate::sendArray,bubble,&BubbleSort::working);
connect(gen,&Generate::sendArray,quick,&QuickSort::working);
//接收生成随机数子线程数据
connect(gen,&Generate::sendArray,this,[=](QVector<int>list){
//排序线程启动
t2->start();
t3->start();
for(int i=0;i<list.size();++i){
ui->randlist->addItem(QString::number(list.at(i)));
}
});
//接收排序后有序序列
connect(bubble,&BubbleSort::finish,this,[=](QVector<int>list){
for(int i=0;i<list.size();++i){
ui->bubblelist->addItem(QString::number(list.at(i)));
}
});
connect(quick,&QuickSort::finish,this,[=](QVector<int>list){
for(int i=0;i<list.size();++i){
ui->quicklist->addItem(QString::number(list.at(i)));
}
});
//回收线程资源
connect(this,&MainWindow::destroyed,this,[=](){
t1->quit();
t1->wait();
t1->deleteLater();
t2->quit();
t2->wait();
t2->deleteLater();
t3->quit();
t3->wait();
t3->deleteLater();
gen->deleteLater();
bubble->deleteLater();
quick->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
这种方法更灵活,可读性更强。

No responses yet