QT中的多线程使用

一、多线程讲解

        在Qt中,多线程编程主要用于实现程序的并发执行,以充分利用多核处理器资源,提高程序执行效率和响应速度。简单的来说就是ui界面一般是主线程,在主线程执行的时候不能执行其他操作,如随机数排序等。如果想一边排序一边拖拽窗口,可以把排序放进子线程里面,这样就可以一边拖拽一边在排序了。

        个人理解,在QT中多线程的实现方式有两种,一种是定义线程类,重写run()函数,还有一种是写个类把动作扔进线程moveToThread()。

        两种方法都要开始线程和释放结束线程,也可以用线程池做小任务的管理,这样只用管线程开始即可,将线程放进线程池,线程池自动管理子线程释放,释放线程后的线程地址可以再次执行新的线程,节约空间

二、实现方法一

1、构建线程类

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QObject>
#include <QThread>
#include <QVector>
#include <QElapsedTimer>
#include <QDebug>

class Generate : public QThread
{
    Q_OBJECT
public:
    explicit Generate(QObject *parent = nullptr);
    void recvNum(int num);

signals:
    void sendArray(QVector<int> num);
private:
    int m_num;

protected:
    void run() override;
};

class BubbleSort : public QThread
{
    Q_OBJECT
public:
    explicit BubbleSort(QObject *parent = nullptr);
    void recvArray(QVector<int> list);

signals:
    void finish(QVector<int> num);
private:
    QVector<int> m_list;

protected:
    void run() override;
};

class QuikSort : public QThread
{
    Q_OBJECT
public:
    explicit QuikSort(QObject *parent = nullptr);
    void recvArray(QVector<int> list);

signals:
    void finish(QVector<int> num);
private:
    void quikSort(QVector<int> &list, int l, int r);
    QVector<int> m_list;


protected:
    void run() override;
};

#endif // MYTHREAD_H

构造三个线程类,分别是随机数的普通排序、冒泡排序、快速排序,注意父类是QThread。并且要重构run()函数。

protected:
    void run() override;

2、编写动作

#include "mythread.h"

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(qrand() % 100000);
    }
    int milsec = time.elapsed();
    qDebug() << "生成" << m_num << "个随机数,用时:" << milsec << "豪秒";
    emit sendArray(list);
}

BubbleSort::BubbleSort(QObject *parent)
{

}

void BubbleSort::recvArray(QVector<int> list)
{
    m_list = list;
}

void BubbleSort::run()
{
    qDebug() << "冒泡排序线程的线程地址: " << QThread::currentThread();
    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);
}

QuikSort::QuikSort(QObject *parent)
{

}

void QuikSort::recvArray(QVector<int> list)
{
    m_list = list;
}

void QuikSort::run()
{
    qDebug() << "快速排序线程的线程地址: " << QThread::currentThread();
    QElapsedTimer time;
    time.start();
    quikSort(m_list, 0, m_list.size()-1);
    int milsec = time.elapsed();
    qDebug() << "快速排序用时:" << milsec << "豪秒";
    emit finish(m_list);
}

void QuikSort::quikSort(QVector<int> &s, int l, int r)
{
    if(l < r)
    {
        int i = l, j = r;
        //拿出第一个元素,保存到x中,第一个位置成为一个坑
        int x = s[l];
        while (i<j)
        {
            //从右向左找小于x的数
            while (i<j && s[j]>=x)
            {
                //左移,直到遇到小于等于x的数
                j--;
            }
            if(i<j)
            {
                //将右侧找到小于x的元素放到左侧坑中,右侧出现一个坑
                //左侧元素索引右移
                s[i++] = s[j];
            }

            //从左到右找大于X的数
            while (i<j && s[i]<x)
            {
                //右移,直到遇到大于x的数
                i++;
            }
            if(i<j)
            {
                //将左侧找到的元素放到右侧坑中,左侧出现一个坑
                //右侧元素索引左移
                s[j--] = s[i];
            }
        }
        //此时i=j,将保存到x的数填入坑中
        s[i] = x;
        quikSort(s, l, i-1);//递归调用
        quikSort(s, i+1, r);//递归调用
    }
}

编写随机数生成(形参)、冒泡排序的排序、快速排序的排序

3、运用

//构建一个线程
    Generate* gen = new Generate;
    BubbleSort* bub = new BubbleSort;
    QuikSort* quk = new QuikSort;

    //确定线程生成的随机数有多少个
    connect(this, &PJQWidget::starting, gen, &Generate::recvNum);
    //启动子线程
    connect(ui->start_btn, &QPushButton::clicked, this, [=]()
    {
        emit starting(10000);
        gen->start();
    });

    connect(gen, &Generate::sendArray, bub, &BubbleSort::recvArray);
    connect(gen, &Generate::sendArray, quk, &QuikSort::recvArray);
    //接收子线程发送的数据
    connect(gen, &Generate::sendArray, this, [=](QVector<int> list)
    {
        bub->start();
        quk->start();
        for (int i=0; i<list.size(); ++i)
        {
            ui->randlist->addItem(QString::number(list.at(i)));
        }
    });


    //接收子线程发送的冒泡排序数据
    connect(bub, &BubbleSort::finish, this, [=](QVector<int> list)
    {
        for (int i=0; i<list.size(); ++i)
        {
            ui->bubblelist->addItem(QString::number(list.at(i)));
        }
    });

    //接收子线程发送的快速排序数据
    connect(quk, &QuikSort::finish, this, [=](QVector<int> list)
    {
        for (int i=0; i<list.size(); ++i)
        {
            ui->quiklist->addItem(QString::number(list.at(i)));
        }
    });

    //线程资源回收
    connect(this, &PJQWidget::destroy, this, [=]()
    {
        gen->quit();
        gen->wait();
        gen->deleteLater(); //delete t1

        bub->quit();
        bub->wait();
        bub->deleteLater();

        quk->quit();
        quk->wait();
        quk->deleteLater();
    });

//确定线程生成的随机数有多少个
    connect(this, &PJQWidget::starting, gen, &Generate::recvNum);
    //启动子线程
    connect(ui->start_btn, &QPushButton::clicked, this, [=]()
    {
        emit starting(10000);
        gen->start();
    });

生成10000个随机数,输入实参10000。

connect(gen, &Generate::sendArray, bub, &BubbleSort::recvArray);
connect(gen, &Generate::sendArray, quk, &QuikSort::recvArray);

输入快速排序和冒泡排序的实参,即Generate生成的10000个随机数。

最后排序结束线程资源回收。

三、实现方法二

1、构建数据处理的类

#ifndef MYTHREAD_2_H
#define MYTHREAD_2_H

#include <QObject>
#include <QThread>
#include <QVector>
#include <QElapsedTimer>
#include <QDebug>

class Generate_2 : public QObject
{
    Q_OBJECT
public:
    explicit Generate_2(QObject *parent = nullptr);
    void work(int num);

signals:
    void sendArray(QVector<int> num);


};

class BubbleSort_2 : public QObject
{
    Q_OBJECT
public:
    explicit BubbleSort_2(QObject *parent = nullptr);
    void work(QVector<int> list);

signals:
    void finish(QVector<int> num);

};

class QuikSort_2 : public QObject
{
    Q_OBJECT
public:
    explicit QuikSort_2(QObject *parent = nullptr);
    void work(QVector<int> list);

signals:
    void finish(QVector<int> num);
private:
    void quikSort(QVector<int> &list, int l, int r);


};

#endif // MYTHREAD_2_H

2、排序动作

#include "mythread_2.h"

Generate_2::Generate_2(QObject *parent) : QObject(parent)
{

}


void Generate_2::work(int num)
{
    qDebug() << "生成随机数线程的线程地址: " << QThread::currentThread();
    QVector<int> list;
    QElapsedTimer time;
    time.start();
    for(int i=0; i<num; ++i)
    {
        list.push_back(qrand() % 100000);
    }
    int milsec = time.elapsed();
    qDebug() << "生成" << num << "个随机数,用时:" << milsec << "豪秒";
    emit sendArray(list);
}

BubbleSort_2::BubbleSort_2(QObject *parent)
{

}


void BubbleSort_2::work(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);
}

QuikSort_2::QuikSort_2(QObject *parent)
{

}

void QuikSort_2::work(QVector<int> list)
{
    qDebug() << "快速排序线程的线程地址: " << QThread::currentThread();
    QElapsedTimer time;
    time.start();
    quikSort(list, 0, list.size()-1);
    int milsec = time.elapsed();
    qDebug() << "快速排序用时:" << milsec << "豪秒";
    emit finish(list);
}

void QuikSort_2::quikSort(QVector<int> &s, int l, int r)
{
    if(l < r)
    {
        int i = l, j = r;
        //拿出第一个元素,保存到x中,第一个位置成为一个坑
        int x = s[l];
        while (i<j)
        {
            //从右向左找小于x的数
            while (i<j && s[j]>=x)
            {
                //左移,直到遇到小于等于x的数
                j--;
            }
            if(i<j)
            {
                //将右侧找到小于x的元素放到左侧坑中,右侧出现一个坑
                //左侧元素索引右移
                s[i++] = s[j];
            }

            //从左到右找大于X的数
            while (i<j && s[i]<x)
            {
                //右移,直到遇到大于x的数
                i++;
            }
            if(i<j)
            {
                //将左侧找到的元素放到右侧坑中,左侧出现一个坑
                //右侧元素索引左移
                s[j--] = s[i];
            }
        }
        //此时i=j,将保存到x的数填入坑中
        s[i] = x;
        quikSort(s, l, i-1);//递归调用
        quikSort(s, i+1, r);//递归调用
    }
}

该方法在实际使用中使用的多一些

3、运用

//构建一个线程
    QThread* t1 = new QThread;
    QThread* t2 = new QThread;
    QThread* t3 = new QThread;

    //创建任务类对象
    Generate_2* gen = new Generate_2;
    BubbleSort_2* bub = new BubbleSort_2;
    QuikSort_2* quk = new QuikSort_2;

    //将任务对象移动到某个子线程中
    gen->moveToThread(t1);
    bub->moveToThread(t2);
    quk->moveToThread(t3);

    //确定线程生成的随机数有多少个
    connect(this, &PJQWidget::starting, gen, &Generate_2::work);
    //启动子线程
    connect(ui->start_btn, &QPushButton::clicked, this, [=]()
    {
        emit starting(10000);
        t1->start();
    });

    connect(gen, &Generate_2::sendArray, bub, &BubbleSort_2::work);
    connect(gen, &Generate_2::sendArray, quk, &QuikSort_2::work);
    //接收子线程发送的数据
    connect(gen, &Generate_2::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(bub, &BubbleSort_2::finish, this, [=](QVector<int> list)
    {
        for (int i=0; i<list.size(); ++i)
        {
            ui->bubblelist->addItem(QString::number(list.at(i)));
        }
    });

    //接收子线程发送的快速排序数据
    connect(quk, &QuikSort_2::finish, this, [=](QVector<int> list)
    {
        for (int i=0; i<list.size(); ++i)
        {
            ui->quiklist->addItem(QString::number(list.at(i)));
        }
    });

    //线程资源回收
    connect(this, &PJQWidget::destroy, this, [=]()
    {
        t1->quit();
        t1->wait();
        t1->deleteLater(); //delete t1

        t2->quit();
        t2->wait();
        t2->deleteLater();

        t3->quit();
        t3->wait();
        t3->deleteLater();

        gen->deleteLater();
        bub->deleteLater();
        quk->deleteLater();
    });

对比方法一,主要是把任务扔进了线程moveToThread(),同样要线程资源回收。

四、线程池

上面的方法都要线程资源回收,而且使用过的线程地址为重复充分利用,针对这种浪费现状,可以使用线程池来管理线程地址,使用中只管线程开始就行,不用考虑县城资源回收。下面以方法二线程为例。

1、构建方法二线程

#ifndef MYTHREAD_POOL_H
#define MYTHREAD_POOL_H

#include <QObject>
#include <QRunnable>
#include <QThread>
#include <QVector>
#include <QElapsedTimer>
#include <QDebug>

class Generate_pool : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit Generate_pool(QObject *parent = nullptr);
    void recvNum(int num);
    void run() override;

signals:
    void sendArray(QVector<int> num);
private:
    int m_num;
};

class BubbleSort_pool : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit BubbleSort_pool(QObject *parent = nullptr);
    void recvArray(QVector<int> list);
    void run() override;

signals:
    void finish(QVector<int> num);
private:
    QVector<int> m_list;


};

class QuikSort_pool : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit QuikSort_pool(QObject *parent = nullptr);
    void recvArray(QVector<int> list);
    void run() override;

signals:
    void finish(QVector<int> num);
private:
    void quikSort(QVector<int> &list, int l, int r);
    QVector<int> m_list;
};

#endif // MYTHREAD_POOL_H
#include "mythread_pool.h"

Generate_pool::Generate_pool(QObject *parent) : QObject(parent)
{
    setAutoDelete(true);
}


void Generate_pool::recvNum(int num)
{
    m_num = num;
}

void Generate_pool::run()
{
    qDebug() << "生成随机数线程的线程地址: " << QThread::currentThread();
    QVector<int> list;
    QElapsedTimer time;
    time.start();
    for(int i=0; i<m_num; ++i)
    {
        list.push_back(qrand() % 100000);
    }
    int milsec = time.elapsed();
    qDebug() << "生成" << m_num << "个随机数,用时:" << milsec << "豪秒";
    emit sendArray(list);
}

BubbleSort_pool::BubbleSort_pool(QObject *parent)
{
    setAutoDelete(true);
}

void BubbleSort_pool::recvArray(QVector<int> list)
{
    m_list = list;
}

void BubbleSort_pool::run()
{
    qDebug() << "冒泡排序线程的线程地址: " << QThread::currentThread();
    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);
}

QuikSort_pool::QuikSort_pool(QObject *parent)
{
    setAutoDelete(true);
}

void QuikSort_pool::recvArray(QVector<int> list)
{
    m_list = list;
}

void QuikSort_pool::run()
{
    qDebug() << "快速排序线程的线程地址: " << QThread::currentThread();
    QElapsedTimer time;
    time.start();
    quikSort(m_list, 0, m_list.size()-1);
    int milsec = time.elapsed();
    qDebug() << "快速排序用时:" << milsec << "豪秒";
    emit finish(m_list);
}

void QuikSort_pool::quikSort(QVector<int> &s, int l, int r)
{
    if(l < r)
    {
        int i = l, j = r;
        //拿出第一个元素,保存到x中,第一个位置成为一个坑
        int x = s[l];
        while (i<j)
        {
            //从右向左找小于x的数
            while (i<j && s[j]>=x)
            {
                //左移,直到遇到小于等于x的数
                j--;
            }
            if(i<j)
            {
                //将右侧找到小于x的元素放到左侧坑中,右侧出现一个坑
                //左侧元素索引右移
                s[i++] = s[j];
            }

            //从左到右找大于X的数
            while (i<j && s[i]<x)
            {
                //右移,直到遇到大于x的数
                i++;
            }
            if(i<j)
            {
                //将左侧找到的元素放到右侧坑中,左侧出现一个坑
                //右侧元素索引左移
                s[j--] = s[i];
            }
        }
        //此时i=j,将保存到x的数填入坑中
        s[i] = x;
        quikSort(s, l, i-1);//递归调用
        quikSort(s, i+1, r);//递归调用
    }
}


2、线程池使用

#ifndef PJTHREADPOOL_H
#define PJTHREADPOOL_H

#include <QWidget>
#include <QThreadPool>

#include "mythread_pool.h"

namespace Ui {
class PJThreadPool;
}

class PJThreadPool : public QWidget
{
    Q_OBJECT

public:
    explicit PJThreadPool(QWidget *parent = nullptr);
    ~PJThreadPool();

private:
    Ui::PJThreadPool *ui;

signals:
    void starting(int num);
};

#endif // PJTHREADPOOL_H
#include "pjthreadpool.h"
#include "ui_pjthreadpool.h"

PJThreadPool::PJThreadPool(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::PJThreadPool)
{
    ui->setupUi(this);

    //构建一个线程
    Generate_pool* gen = new Generate_pool;
    BubbleSort_pool* bub = new BubbleSort_pool;
    QuikSort_pool* quk = new QuikSort_pool;

    //确定线程生成的随机数有多少个
    connect(this, &PJThreadPool::starting, gen, &Generate_pool::recvNum);
    //启动子线程
    connect(ui->pushButton_start, &QPushButton::clicked, this, [=]()
    {
        emit starting(10000);
        //将运行的线程扔进线程池
        QThreadPool::globalInstance()->start(gen);
    });

    connect(gen, &Generate_pool::sendArray, bub, &BubbleSort_pool::recvArray);
    connect(gen, &Generate_pool::sendArray, quk, &QuikSort_pool::recvArray);
    //接收子线程发送的数据
    connect(gen, &Generate_pool::sendArray, this, [=](QVector<int> list)
    {
        QThreadPool::globalInstance()->start(bub);
        QThreadPool::globalInstance()->start(quk);
        for (int i=0; i<list.size(); ++i)
        {
            ui->listWidget_rand->addItem(QString::number(list.at(i)));
        }
    });


    //接收子线程发送的冒泡排序数据
    connect(bub, &BubbleSort_pool::finish, this, [=](QVector<int> list)
    {
        for (int i=0; i<list.size(); ++i)
        {
            ui->listWidget_bubble->addItem(QString::number(list.at(i)));
        }
    });

    //接收子线程发送的快速排序数据
    connect(quk, &QuikSort_pool::finish, this, [=](QVector<int> list)
    {
        for (int i=0; i<list.size(); ++i)
        {
            ui->listWidget_quik->addItem(QString::number(list.at(i)));
        }
    });
}


PJThreadPool::~PJThreadPool()
{
    delete ui;
}

其中

//将运行的线程扔进线程池
        QThreadPool::globalInstance()->start(gen);

这个操作为线程池使用,然后就可以不用管了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值