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(); }
长风破浪会有时,直挂云帆济沧海!
可通过下方链接找到博主
https://www.cnblogs.com/judes/p/10875138.html