Coffee_Candy

92 分类: Qt软件开发

QT6线程池

为什么用线程池

在有一堆需要异步长时间处理的事件的情况下,每次创建一个子线程和结束销毁一个子线程开销是比较大的
那么这种情况下就应该按照系统所对应的核数,创建对应核数的线程池,来运行QRunnable,
线程池在整个程序结束前不销毁,即保留对应数量的线程,只做事件的调入调出

QT6线程池使用

QT6对线程池封装了对应的单例类(比纯C++手写方便了不少)
我们只需要在main文件包含#include,#inlcude
获取线程池单例,并设置最大线程数,也就是QThread::idealThreadCount()获取理想的线程数量也就是系统cpu核数

QThreadPool::globalInstance()->setMaxThreadCount(QThread::idealThreadCount());
  1. 定义一个继承自QRunnable和QObject的类

也就是我们实际运行的事件,然后重写virtual run()和QThread做法差不多
区别就是在于QRunnable其本身不继承自QObject,无法发送信号,so在写多类的继承的时候一定要把QObject的继承写在最前面

#include <QRunnable>
#include <QObject>
#include "global.h"
class Imagethread:public QObject, public QRunnable,std::enable_shared_from_this<Imagethread>
{
    Q_OBJECT
public:
    Imagethread(const QStringList imagelist,QObject *parent=nullptr);
    ~Imagethread();
    QStringList* imageswitchbase64();
protected:
    virtual void run();
private:
    QStringList* imagebase64list=nullptr;
    bool _bstop=false;
    QStringList imagelist;
signals:
    void SigFinishImageSwitchBase64(QStringList* imagebase64list);
};

然后比较重要的一点是如果涉及到信号发送的情况并且你使用的是指针的情况下,请务必把setAutoDelete(false)置为false为什么后面会讲

#include "Imagethread.h"
#include <QBuffer>
#include <QPixmap>
Imagethread::Imagethread(const QStringList imagelist, QObject* parent):
    QObject(parent), QRunnable(), imagelist(imagelist)
{
    setAutoDelete(false);
}

Imagethread::~Imagethread()
{
    if (imagebase64list != nullptr) delete imagebase64list;
//这个析构打印后面也会涉及到
    qDebug() << "imagethread end";
}

QStringList* Imagethread::imageswitchbase64()
{
    imagebase64list = new QStringList();
    if (this->imagelist.size() > 0)
    {
        for (QString imageurl : this->imagelist)
        {
            QPixmap tpimage(imageurl);
            QByteArray byteArray;
            QBuffer buffer(&byteArray);
            buffer.open(QIODevice::WriteOnly);
            tpimage.save(&buffer, "JPG");
            QString base64Data = byteArray.toBase64();
            imagebase64list->append(base64Data);
            qDebug() <<"size:" << imagebase64list->size();
        }
        return imagebase64list;
    }
    return nullptr;
}

void Imagethread::run()
{
    qDebug() << "run";
    QStringList* imagebase64list=imageswitchbase64();
    if (imagebase64list != nullptr)
    {
        qDebug() << "size:" << imagebase64list->size();
    }
    if (_bstop)
    {
        delete imagebase64list;
        return;
    }
    emit SigFinishImageSwitchBase64(imagebase64list);
}

代码是从上一个多线程代码改过来的基本上差不多,参数设置同上一篇文章

  1. 主线程中使用

还是一样在.h文件中去定义一个智能指针去管理这个事件,这个智能指针在整个程序结束时会自动把里面的东西析构掉,也就是说我们不能在他自己析构前去析构,这也是为什么我前面说不要设置Runnable的自动delete的原因1
2024-10-25T03:40:48.png
然后在.cpp文件中这样子调用

把QRunnable的事件构造出来,绑定完成的信号接收,并且设置Qt::QueuedConnection,这样子子线程和主线程就可以跨线程通信,然后QThreadPool::globalInstance()->start(imagethread.get());绑定对应的事件启动线程

你会发现imagethread.get()传递了智能指针的裸指针过去,这也是为什么我前面说不要设置Runnable的自动delete的原因2,如果我在事件结束了的时候对这个裸指针进行了析构,那么会导致信号发送到主线程时connect(imagethread.get(),&Imagethread::SigFinishImageSwitchBase64,this,&T_Addfdc::getimage64list,Qt::QueuedConnection); imagethread.get()变为nullptr了,这显然是不对的,其实我们并不需要让QRunnable去管理我们的指针,因为我们已经有智能指针在管理,也就是脚踏两条船,两岸相隔没什么事,一但同时会面就会翻船

void T_Addfdc::addsqldata()
{
    qDebug() << QString::fromLocal8Bit("确认添加数据 ");
    imagethread = std::make_shared<Imagethread>(this->nowcardlist, this);
    connect(imagethread.get(), &Imagethread::SigFinishImageSwitchBase64,this, &T_Addfdc::getimage64list, Qt::QueuedConnection);
    QThreadPool::globalInstance()->start(imagethread.get());
}

emm但是这样子的话会导致imagethread在这次用完之后不会析构掉,而是等下一次new的时候,实在觉得难受可以在槽函数里面去智能指针.reset()一下
但是怎么说呢,程序结束的时候,也就是走出.h的代码域时,智能指针的引用计数会变为0,也就是会打印这个,其实我觉得没什么大不了的
2024-10-25T04:00:41.png

#none

作者: Coffee_Candy

版权: 除特别声明,均采用BY-NC-SA 4.0许可协议,转载请表明出处

目录Content

评论已关闭