1.为什么使用多线程
使用了多线程之后能够减少界面的卡顿,如果是单线程,假设我在窗口里写上一个排序,开始执行时我在拖动窗口就会未响应,因为主线程在处理排序不会响应.我们可以使用多线程来解决,在子线程里进行排序,这样就可以拖动窗口.在主线程中不能进行复杂的操作.
2.qt中的线程Qthread
在手册中可以查看相关的说明和函数,线程之间可以设置优先级
// Qt中的线程可以设置优先级
// 得到当前线程的优先级
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);
线程的退出,一般都是两个搭配着用
//退出线程
Void QThread::exit(int returnCode = 0);
//等待任务完成退出线程
Bool QThread::wait()
信号和槽函数
子线程处理任务的函数run(),需要把任务写到这个函数里,通过
void Qthread::started()
来启动
3.线程的使用方式一
3.1使用方式一
1.创建一个线程类的子类,继承QT的Qthread,并且重写父类的run()方法
class MyThread:public QThread
{
protect:
void run(){}
}
2.在主线程(主界面)中创建子线程对象,new一个,并且调用start()启动线程
MyThread * subThread = new MyThread;
subThread->start();
3.2 搭建测试主界面
如下图搭建测试的主界面
3.3 编写线程子类
新建class Mythread继承自QObject,将它修改为继承QThread.首先我们接受一个num作为产生的随机数个数,然后再run中编写业务代码也就是生成随机数,最后在把所有的随机数存储在vector容器中,用信号将容器的值发送出去
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QThread>
//生成随机数
class Generate_random : public QThread
{
Q_OBJECT
public:
explicit Generate_random(QObject *parent = nullptr);
void recvNum(int num);
protected:
//处理业务代码
void run() override;
int m_num;
signals:
//发送随机数容器
void sendList(QVector<int> list);
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
#include <QElapsedTimer>
// 构造函数,初始化线程对象
Generate_random::Generate_random(QObject *parent)
: QThread{parent}
{}
// 接受随机数的个数
void Generate_random::recvNum(int num)
{
m_num = num;
}
// 线程运行函数
void Generate_random::run()
{
// 输出当前线程号
qDebug() << "生成随机数的线程号:" << QThread::currentThread();
// 定义存储随机数的容器
QVector<int> list;
// 定义计时器
QElapsedTimer time;
time.start(); // 开始计时
// 生成随机数
for(int i = 0; i < m_num; i++)
{
list.push_back(qrand() % 100000); // 生成0到99999之间的随机数
}
// 计算生成随机数所用时间
int costtime = time.elapsed();
qDebug() << "生成" << m_num << "个随机数用的时间:" << costtime << "毫秒";
// 发送生成的随机数列表
emit sendList(list);
}
3.4 编写主函数
首先创建子线程对象,通过槽函数把生成随机数的个数用信号发送给子线程.当点击按钮时发送信号并启动子线程.当生成随机数完毕后,将生成的随机数显示到list控件上
要注意子线程没办法直接发送数据到主线程,必须要经过信号才能发送数据
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
Ui::MainWindow *ui;
signals:
void starting(int num);
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mythread.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//1.创建子线程对象
Generate_random *gennum = new Generate_random;
connect(this,&MainWindow::starting,gennum,&Generate_random::recvNum);
//2.启动子线程
connect(ui->btn_start,&QPushButton::clicked,[=](){
emit starting(100000);
gennum->start();
});
//3.接受子线程发送的数据
connect(gennum,&Generate_random::sendList,[=](QVector<int> list){
for (int var = 0; var < list.size(); ++var) {
ui->randList->addItem(QString::number(list.at(var)));
}
});
}
MainWindow::~MainWindow()
{
delete ui;
}
若是出现listwidget里没有数字,并且给出了这个提示
出现这个错误是因为
QVector<int>
类型未注册为 Qt 的元类型,无法在信号槽机制中使用。要解决这个问题,需要使用 qRegisterMetaType 注册 QVector<int>
类型。在main.cpp里添加上下边的代码// 注册 QVector 类型 qRegisterMetaType>("QVector");
3.5 冒泡排序
将刚刚写好的线程类复制一个,修改为BubleSort,将run函数修改为冒泡排序,接收发送的随机数容器vector,排序后发送排序完的容器
mythread.h
class BUbleSort : public QThread
{
Q_OBJECT
public:
explicit BUbleSort(QObject *parent = nullptr);
void recvList(QVector<int> list);
protected:
//处理业务代码
void run() override;
QVector<int> m_list;
signals:
//发送排序后的结果
void finishSortList(QVector<int> list);
};
mythread.cpp
// 构造函数,初始化线程对象
BUbleSort::BUbleSort(QObject *parent) : QThread{parent}
{
}
// 接收需要排序的列表
void BUbleSort::recvList(QVector<int> list)
{
m_list = list;
}
// 线程运行函数
void BUbleSort::run()
{
int temp;
// 输出当前线程号
qDebug() << "冒泡排序的线程号:" << QThread::currentThread();
// 定义计时器
QElapsedTimer time;
time.start(); // 开始计时
qDebug()<<m_list;
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;
qDebug()<< m_list[j];
}
}
}
// 发送排序后的列表
// 计算生成随机数所用时间
int costtime = time.elapsed();
qDebug() <<"冒泡排序的时间:" << costtime << "毫秒";
emit finishSortList(m_list);
}
3.6 启动冒泡排序
我们需要再mainwindow.cpp里边进行信号关联,首先创建冒泡排序的线程对象,然后要接受随机数容器就是sendList信号绑定槽函数,当我们在界面上把随机数都显示出来时说明随机数生成完成了,那么就可以启动冒泡排序bubsort->start();
,最后再将冒泡排序的结果显示到界面上.
流程:接受数据容器->启动冒泡排序->展示结果
BUbleSort *bubsort = new BUbleSort;
......
//3.接受子线程发送的数据
//冒泡排序接受数据
connect(gennum,&Generate_random::sendList,bubsort,&BUbleSort::recvList);
connect(gennum,&Generate_random::sendList,[=](QVector<int> list){
for (int var = 0; var < list.size(); ++var) {
ui->randList->addItem(QString::number(list.at(var)));
}
//启动冒泡排序
bubsort->start();
});
//4.接受冒泡排序并显示
connect(bubsort,&BUbleSort::finishSortList,[=](QVector<int> list){
for (int var = 0; var < list.size(); ++var) {
ui->bubList->addItem(QString::number(list.at(var)));
}
});
3.7 快速排序
逻辑都更冒泡排序一样,直接复制,修改排序代码即可
mythread.h
//快速排序
class QuickleSort : public QThread
{
Q_OBJECT
public:
explicit QuickleSort(QObject *parent = nullptr);
void recvList(QVector<int> list);
protected:
//处理业务代码
void run() override;
void quicksort(QVector<int> &list,int l , int r);
QVector<int> m_list;
signals:
//发送排序后的结果
void finishSortList(QVector<int> list);
};
mythread.cpp
QuickleSort::QuickleSort(QObject *parent)
{
}
void QuickleSort::recvList(QVector<int> list)
{
m_list = list;
}
void QuickleSort::run()
{
// 输出当前线程号
qDebug() << "快速排序的线程号:" << QThread::currentThread();
// 定义计时器
QElapsedTimer time;
time.start(); // 开始计时
quicksort(m_list,0,m_list.size()-1);
int costtime = time.elapsed();
qDebug() <<"快速排序的时间:" << costtime << "毫秒";
emit finishSortList(m_list);
}
void QuickleSort::quicksort(QVector<int> &list, int l, int r)
{
if (l >= r) return; // 基本条件,区间内少于两个元素时返回
int pivot = list[l]; // 选择第一个元素作为基准
int low = l + 1; // 左指针从基准的下一个元素开始
int high = r; // 右指针从最右边开始
while (low <= high) // 左右指针相遇时结束循环
{
while (low <= high && list[low] <= pivot)
{
low++; // 找到一个比基准大的元素
}
while (low <= high && list[high] >= pivot)
{
high--; // 找到一个比基准小的元素
}
if (low < high)
{
std::swap(list[low], list[high]); // 交换这两个元素
}
}
std::swap(list[l], list[high]); // 将基准元素移到正确的位置
quicksort(list, l, high - 1); // 对基准左侧的子序列递归调用
quicksort(list, high + 1, r); // 对基准右侧的子序列递归调用
}
mainwindow.cpp里边的逻辑跟冒泡排序中的一样
流程:接受数据容器->启动快速排序->展示结果
QuickleSort *quicksort = new QuickleSort;
connect(gennum,&Generate_random::sendList,[=](QVector<int> list){
//启动冒泡排序
bubsort->start();
//启动快速排序
quicksort->start();
for (int var = 0; var < list.size(); ++var) {
ui->randList->addItem(QString::number(list.at(var)));
}
});
connect(quicksort,&QuickleSort::finishSortList,[=](QVector<int> list){
for (int var = 0; var < list.size(); ++var) {
ui->quickList->addItem(QString::number(list.at(var)));
}
});
4 线程的使用方式二
4.1 具体操作
1.创建一个新的类,继承自QObject,在类中添加一个公共成员函数working(就是执行业务逻辑的函数跟run一样)
必须继承自QObject,才能调用moveToThread
class MyWork:public QObject
{....}
2.在主线程中创建一个QThread对象,这就是子线程,并且创建业务逻辑的对象
QThread *subthread = new QThread
MyWork* work = new MyWork;
//错误的创建方式,不需要指定父类
MyWork* work = new MyWork(this);
3.将MyWork对象移动到子线程中,使用moveToThread()方法
work->moveToThread(subthread);
4.启动子线程,在需要的地方调用工作函数,这样就会让工作函数在子线程中运行
sub->start;
work->working();
4.2 修改线程代码
复制一份上边的代码文件夹,修改.让mythread的类都继承自QObject,将业务逻辑处理代码run变为working同时变成public函数,这样我们就不需要在用类内成员传递参数,直接在外部传递参数就可以
mythread.h
//生成随机数
class Generate_random : public QObject
{
Q_OBJECT
public:
explicit Generate_random(QObject *parent = nullptr);
//处理业务代码
void working(int num);
signals:
//发送随机数容器
void sendList(QVector<int> list);
};
//冒泡排序
class BUbleSort : public QObject
{
Q_OBJECT
public:
explicit BUbleSort(QObject *parent = nullptr);
//处理业务代码
void working(QVector<int> list);
signals:
//发送排序后的结果
void finishSortList(QVector<int> list);
};
//快速排序
class QuickleSort : public QObject
{
Q_OBJECT
public:
explicit QuickleSort(QObject *parent = nullptr);
//处理业务代码
void working(QVector<int> list);
protected:
void quicksort(QVector<int> &list,int l , int r);
signals:
//发送排序后的结果
void finishSortList(QVector<int> list);
};
mythread.cpp
// 构造函数,初始化线程对象
Generate_random::Generate_random(QObject *parent)
: QObject{parent}
{}
// 线程运行函数
void Generate_random::working(int num)
{
// 输出当前线程号
qDebug() << "生成随机数的线程号:" << QThread::currentThread();
// 定义存储随机数的容器
QVector<int> list;
// 定义计时器
QElapsedTimer time;
time.start(); // 开始计时
// 生成随机数,使用随机种子
qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
for(int i = 0; i < num; i++)
{
list.push_back(qrand() % 10000); // 生成0到9999之间的随机数
}
// 计算生成随机数所用时间
int costtime = time.elapsed();
qDebug() << "生成" << num << "个随机数用的时间:" << costtime << "毫秒";
// 发送生成的随机数列表
emit sendList(list);
}
// 构造函数,初始化线程对象
BUbleSort::BUbleSort(QObject *parent) : QObject{parent}
{
}
// 线程运行函数
void BUbleSort::working(QVector<int> list)
{
int temp;
// 输出当前线程号
qDebug() << "冒泡排序的线程号:" << QThread::currentThread();
// 定义计时器
QElapsedTimer time;
time.start(); // 开始计时
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 costtime = time.elapsed();
qDebug() <<"冒泡排序的时间:" << costtime << "毫秒";
emit finishSortList(list);
}
QuickleSort::QuickleSort(QObject *parent)
{
}
void QuickleSort::working(QVector<int> list)
{
// 输出当前线程号
qDebug() << "快速排序的线程号:" << QThread::currentThread();
// 定义计时器
QElapsedTimer time;
time.start(); // 开始计时
quicksort(list,0,list.size()-1);
int costtime = time.elapsed();
qDebug() <<"快速排序的时间:" << costtime << "毫秒";
emit finishSortList(list);
}
void QuickleSort::quicksort(QVector<int> &list, int l, int r)
{
if (l >= r) return; // 基本条件,区间内少于两个元素时返回
int pivot = list[l]; // 选择第一个元素作为基准
int low = l + 1; // 左指针从基准的下一个元素开始
int high = r; // 右指针从最右边开始
while (low <= high) // 左右指针相遇时结束循环
{
while (low <= high && list[low] <= pivot)
{
low++; // 找到一个比基准大的元素
}
while (low <= high && list[high] >= pivot)
{
high--; // 找到一个比基准小的元素
}
if (low < high)
{
std::swap(list[low], list[high]); // 交换这两个元素
}
}
std::swap(list[l], list[high]); // 将基准元素移到正确的位置
quicksort(list, l, high - 1); // 对基准左侧的子序列递归调用
quicksort(list, high + 1, r); // 对基准右侧的子序列递归调用
}
4.3 主线程对象创建
1.创建子线程对象,创建任务类的对象
//1.创建子线程对象
QThread *t1 = new QThread;
QThread *t2 = new QThread;
QThread *t3 = new QThread;
//2.创建任务类对象
Generate_random *gennum = new Generate_random;
BUbleSort *bubsort = new BUbleSort;
QuickleSort *quicksort = new QuickleSort;
2.点击按钮时启动t1线程,同时发送信号开始生成随机数
connect(this,&MainWindow::starting,gennum,&Generate_random::working);
//2.启动子线程
connect(ui->btn_start,&QPushButton::clicked,[=](){
emit starting(10000);
t1->start();
});
3.接受随机数开始排序
//4.接受子线程发送的数据
//冒泡排序接受数据
connect(gennum,&Generate_random::sendList,bubsort,&BUbleSort::working);
//快速排序接受数据
connect(gennum,&Generate_random::sendList,quicksort,&QuickleSort::working);
connect(gennum,&Generate_random::sendList,[=](QVector<int> list){
//启动冒泡排序
t2->start();
//启动快速排序
t3->start();
for (int var = 0; var < list.size(); ++var) {
ui->randList->addItem(QString::number(list.at(var)));
}
});
//4.接受冒泡排序和快速排序并显示
connect(bubsort,&BUbleSort::finishSortList,[=](QVector<int> list){
for (int var = 0; var < list.size(); ++var) {
ui->bubList->addItem(QString::number(list.at(var)));
}
});
connect(quicksort,&QuickleSort::finishSortList,[=](QVector<int> list){
for (int var = 0; var < list.size(); ++var) {
ui->quickList->addItem(QString::number(list.at(var)));
}
});
5 线程的销毁
1.线程对象指定父类this,这样在析构的时候会调用,自动释放
QThread *t1 = new QThread(this);
QThread *t2 = new QThread;
2.使用槽函数析构
当窗口关闭是会发出destroy信号
connect(this,&MainWindow::destroyed,[=](){
t1->quit();
t1->wait();
t1->deleteLater();
t2->quit();
t2->wait();
t2->deleteLater();
t3->quit();
t3->wait();
t3->deleteLater();
gennum->deleteLater();
bubsort->deleteLater();
quicksort->deleteLater();
});