Qt线程池+生产者消费者模型

1、模板类queue,包含头文件<queue>中,是一个FIFO队列。

queue.push():在队列尾巴增加数据
queue.pop():移除队列头部数据
queue.font():获取队列头部数据的引用
...

2、Qt库的线程池,QThreadPool

QThreadPool.setMaxThreadCount():设置线程池最大线程数
QThreadPool.start(new QRunnable(..)):开启线程池调用QRunnable

3、QRunnable执行任务

void run();//重写虚函数,在里面消费任务队列
setAutoDelete(true)//默认就是true,消费结束自动回收内存

4、代码

run.h

#ifndef RUN_H
#define RUN_H

#include <QObject>
#include <QRunnable>
#include <string>
#include <iostream>

struct MyString
{
    std::string valueStr;
};

class Run : public QObject , public QRunnable
{
    Q_OBJECT
public:
    Run() = default;
    Run(const MyString& myString);
protected:
    ~Run() = default;
    void run();
signals:

public slots:

private:
    MyString myString;
};

#endif // RUN_H

说明:
MyString结构体代替实际项目中的任务,Run接口的run纯虚函数用来消费分配来的MyString
run.cpp
#include "run.h"
#include <QThread>
#include <QDebug>
Run::Run(const MyString &myString)
{
    this->myString = myString;
    //this->setAutoDelete(true);//默认就是true
}

void Run::run()
{
    //std::cout << "value:" << this->myString.valueStr <<";调用线程ID为:" << QThread::currentThread() << std::endl;
    qDebug() << "value:" << QString::fromStdString(myString.valueStr) << "thread:" << QThread::currentThreadId();
    QThread::msleep(100);
}
说明:不使用cout打印是因为,cout打印不是原子操作,可能多个字符串被杂糅在一起打印;qDebug不会,应该底层加了锁

main.cpp

#include <QCoreApplication>
#include "run.h"
#include <queue>
#include <mutex>
#include <QThreadPool>
#include <thread>
using namespace std;
queue<MyString> myList;
mutex myMutex;
volatile bool addThreadIsEnd = false;
void makeListThread();
int main(int argc, char *argv[])
{   
    QCoreApplication a(argc, argv);
    cout << "begin main" << endl;
    thread addThread(makeListThread);
    addThread.detach();
    cout << "begin addThread" << endl;
    QThreadPool tp;
    tp.setMaxThreadCount(20);
    cout << "begin threadPool" << endl;
    while(true)
    {
        if(!myList.empty())
        {
            MyString tempMyString = myList.front();
            tp.start(new Run(tempMyString));
            myMutex.lock();
            myList.pop();
            myMutex.unlock();
        }
        else
        {
            if(addThreadIsEnd)
            {
                break;
            }
            else
            {
                QThread::msleep(10);
            }
        }
    }
    cout << "end main,list size:" << myList.size() << endl;
    return a.exec();
}

void makeListThread()
{
    string a;
    MyString tempMyString;
    for(int i=0;i<10000;i++)
    {
        QThread::msleep(0);
        a = to_string(i);
        tempMyString.valueStr = a;
        myMutex.lock();
        myList.push(tempMyString);
        myMutex.unlock();
    }
    addThreadIsEnd = true;
    cout << "end addThread" << endl;
}
5、模型

 6、其他说明

6.1、假设线程池大小有n个,那么这n个线程在线程池初始化的时候就已经定了,即n个线程id是恒定的,队列永远由这n个线程消费

6.2、std::queue非线程安全,同时往队列加任务、取任务可能会触发线程安全问题;同时删除头任务、访问头任务也可能会触发线程安全问题,需要加线程锁

6.3、tp.start(new Run(tempMyString));这里new了一个没有指针指向的Runnable对象,在哪里回收的呢?Run.setAutoDelete(true)自动回收

7例子:

7.1.生产

#ifndef SWEEP_H
#define SWEEP_H

#include <QObject>

class Sweep : public QObject
{
    Q_OBJECT
public:
    explicit Sweep(QObject *parent = nullptr);

    void exec();

private:
    void createFile();

signals:
    void addFile(const QString& filePath);

private:

};

#endif // SWEEP_H




#include "Sweep.h"
#include <thread>
#include <QThread>
#include <QDebug>

Sweep::Sweep(QObject *parent)
    : QObject{parent}
{

}

void Sweep::exec()
{
    std::thread t(&Sweep::createFile, this);
    t.detach();
}

void Sweep::createFile()
{
    //qDebug()<<"createFile thread:"<<QThread::currentThreadId();
    int s4pFile = 100;

    for(int i=0; i<s4pFile; ++i)
    {
        qDebug()<<"sweep number "<<i+1<< " times";
        QThread::msleep(100);
        qDebug()<<"one file created";

        emit this->addFile(QString("D:\\%1.s4p").arg(i+1));
    }
    qDebug()<<"all file created";

}

7.2.Analys队列管理

#ifndef ANALYS_H
#define ANALYS_H

#include <QObject>
#include <QQueue>
#include <QMutex>
#include <QThreadPool>
#include <QMutexLocker>
#include <QWaitCondition>

class Analys : public QObject
{
    Q_OBJECT
public:
    explicit Analys(QObject *parent = nullptr);



    void exec();
signals:

public slots:
    void enqueueString(const QString &str);

private:
    void doTask();

private:
    QQueue<QString> m_taskQueue;
    QMutex m_mutex;

    QThreadPool m_threadPool;
    QWaitCondition m_condition;
};

#endif // ANALYS_H



#include "Analys.h"
#include <QDebug>
#include "AnalysisTask.h"


Analys::Analys(QObject *parent)
    : QObject{parent}
{
    m_threadPool.setMaxThreadCount(10);
    m_threadPool.setExpiryTimeout(30000); // 30秒后回收空闲线程
    m_threadPool.setStackSize(0); // 使用默认栈大小
}

void Analys::exec()
{
    std::thread t(&Analys::doTask, this);
    t.detach();
}

void Analys::enqueueString(const QString &str)
{
    //qDebug()<<"enqueueString thread:"<<QThread::currentThreadId();
    QMutexLocker locker(&m_mutex);
    m_taskQueue.enqueue(str);
//    m_condition.wakeOne();
    m_condition.wakeAll();

    qDebug() << "加入队列:" << m_taskQueue.length();
}

void Analys::doTask()
{
    while(true)
    {
        QMutexLocker locker(&m_mutex);
        while (m_taskQueue.isEmpty())
        {
            m_condition.wait(&m_mutex);
        }

        QString str = m_taskQueue.dequeue();
        locker.unlock(); // 提前解锁:后续提交线程池无需持有锁,提高并发效率
        qDebug() << "从队列取出处理:" << str << "剩余队列:" << m_taskQueue.length();

        // 创建任务并提交到线程池
        AnalysisTask *task = new AnalysisTask(str);
        m_threadPool.start(task);
    }

}

7.3.任务

#ifndef ANALYSISTASK_H
#define ANALYSISTASK_H

#include <QObject>
#include <QRunnable>
class AnalysisTask : public QRunnable
{
public:
    explicit AnalysisTask(const QString &str);
    void run() override;

private:
    QString m_string;
};
#endif // ANALYSISTASK_H



#include "AnalysisTask.h"
#include <QDebug>
#include <QThread>
#include <QTime>


AnalysisTask::AnalysisTask(const QString &str)
    : m_string{str}
{
    this->setAutoDelete(true);
}

void AnalysisTask::run()
{
    qDebug() << "[" << QThread::currentThreadId()
             << "] 线程池开始处理:" << m_string
             << "时间:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    // 模拟2秒处理时间
    QThread::msleep(4000);

    qDebug() << "[" << QThread::currentThreadId()
             << "] 线程池处理完成:" << m_string
             << "时间:" << QTime::currentTime().toString("hh:mm:ss.zzz");

}

7.4.控制

#ifndef CONTROL_H
#define CONTROL_H

#include <QObject>
#include <QThread>

#include "Sweep.h"
#include "Analys.h"

class Control : public QObject
{
    Q_OBJECT
public:
    explicit Control(QObject *parent = nullptr);

    void start();
signals:


private:
    Sweep m_sweep;
    Analys m_analys;

    QThread m_controlThread;
};

#endif // CONTROL_H


#include "Control.h"
#include <QDebug>
#include <QTime>

Control::Control(QObject *parent)
    : QObject{parent}
{
//    QObject::connect(&m_sweep, &Sweep::addFile, [](const QString& filePath){
//        qDebug()<< "create task:" << filePath;
//    });

    m_sweep.moveToThread(&m_controlThread);
    m_analys.moveToThread(&m_controlThread);
    m_controlThread.start();

    QObject::connect(&m_sweep, &Sweep::addFile, &m_analys, &Analys::enqueueString);
}

void Control::start()
{
    qDebug()<<"开始时间:"<<QTime::currentTime().toString("hh:mm:ss.zzz");;
    m_analys.exec();
    m_sweep.exec();

}

 

posted @ 2019-06-12 23:25  朱小勇  阅读(4792)  评论(0)    收藏  举报