QT—-多线程和线程池

1.为什么使用多线程

使用了多线程之后能够减少界面的卡顿,如果是单线程,假设我在窗口里写上一个排序,开始执行时我在拖动窗口就会未响应,因为主线程在处理排序不会响应.我们可以使用多线程来解决,在子线程里进行排序,这样就可以拖动窗口.在主线程中不能进行复杂的操作.

2.qt中的线程Qthread

在手册中可以查看相关的说明和函数,线程之间可以设置优先级

file

// Qt中的线程可以设置优先级
// 得到当前线程的优先级
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);

线程的退出,一般都是两个搭配着用

//退出线程
Void QThread::exit(int returnCode = 0);
//等待任务完成退出线程
Bool QThread::wait()

信号和槽函数

file

子线程处理任务的函数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 搭建测试主界面

如下图搭建测试的主界面

file

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里没有数字,并且给出了这个提示

file

出现这个错误是因为 QVector<int> 类型未注册为 Qt 的元类型,无法在信号槽机制中使用。要解决这个问题,需要使用 qRegisterMetaType 注册 QVector<int> 类型。在main.cpp里添加上下边的代码
// 注册 QVector 类型 qRegisterMetaType>("QVector");
file

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)));
    }
});

file

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)));
    }
});

file

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();
});

6 线程池

如果觉得本文对您有所帮助,可以支持下博主,—分也是缘。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇

超多性价比流量卡,扫码查看

这将关闭于 20