QT多线程练习:单生产者多消费者
- 代码思路
- 1.全局变量
- 2.消费者线程
- 3.消费者管理类
- 4.生产者线程
- 5.主线程
- 总结
最近在研究qt的线程的一些知识点,从如何新建线程到以不同的方式去新建线程,再到多线程以及多线程同步的几种方式等。
看了很多网上的demo最后都通过实现消费者和生产者模型来作为一个总结的练习。但是大部分帖子只是展示了代码和大致思路又或者在里面添加了一些内存和较难懂的线程指针操作,对新手很不友好读起来很费劲,所以自己完成了这个的demo后总结一下。
代码思路
先想好练习的demo是需要涉及 线程创建与退出、线程暂停、父子线程之前以及兄弟线程之间的参数和信号传递、多线程的以及多线程的管理。要求是练习的demo对于以上的点只要涉及基础即可。
- 先想好要创建哪些类:主线程、生产者线程(一)、消费者管理线程(一)、消费者线程(多),以及一些全局变量。
- 主线程职责:UI控件的控制和显示、控制生产者:调节生产的速度和暂停与继续、控制消费
- 生产者线程职责:根据商品数量调控生产,根据生产速度增加商品数量。
- 消费者管理类:根据UI的控制去创建消费者线程以及删除/停止消费者线程,需要一个容器将已存在的消费者线程指针存储起来,并且可以通过容器内的指针去控制消费者线程。
- 消费者线程职责:减少商品数量,其他的都不管,全交给管理类去控制。
- 全局变量:
int StoreSum;//商品库存
Custom_sum;//消费者线程的数量
QMutex Mutex;//互斥锁,防止消费者和生产者同时修改商品库存的值
//其他变量在详细设计的时候再去添加
1.全局变量
单独把一些全局变量和共用的头文件放在appdata.h/.cpp 里面,这样在写的时候结构可以比较清晰,一般的成熟项目都这么写,严禁一点的还可以创建一个类把变量设置为私有的,再通过get/set去取值和修改。
appdata.h
#ifndef APPDATA_H
#define APPDATA_H
#include <QTime>
#include <QThread>
#include <QTimerEvent>
#include <QString>
#include <QDebug>
#include <QMutex>
#include <QList>
extern int StoreSum;
extern int StoreSum_Max;
extern int StoreSum_Min;
extern int Produce_speed;
extern int Produce_speedMax;
extern int Produce_speedMin;
extern int Custom_sum;
extern QMutex Mutex;
#endif
appdata.cpp
#include "appdata.h"
int StoreSum = 20;
int StoreSum_Max = 50;
int StoreSum_Min = 10;
int Produce_speed = 10;
int Produce_speedMax = 15;
int Produce_speedMin = 5;
int Custom_sum = 0;
QMutex Mutex;
2.消费者线程
消费者线程需要完成的事情比较单一,只需要获取全局变量中的StoreSum并且减1即可。
custom.h
#ifndef CUSTOM_H
#define CUSTOM_H
#include <QObject>
#include "appdata.h"
class Custom : public QThread
{
Q_OBJECT
public:
Custom();
bool isRun;
protected:
void run();
};
#endif
custom.cpp
#include "custom.h"
Custom::Custom()
{
isRun = 1;
}
void Custom::run()
{
while(isRun)
{
Mutex.lock();
if(StoreSum>StoreSum_Min)
{
StoreSum--;
}
Mutex.unlock();
sleep(1);
}
}
3.消费者管理类
主线程通过消费者管理类去创建和退出消费者线程,相当于是一个专门管理的类。
custom.h
#ifndef CUSTOMMANAGER_H
#define CUSTOMMANAGER_H
#include "appdata.h"
#include "custom.h"
class CustomManager
{
public:
CustomManager();
void creatCustom();
void delCustom();
private:
QList<Custom *> customList;
};
#endif
custom.cpp
#include "custommanager.h"
CustomManager::CustomManager()
{
}
void CustomManager::creatCustom()
{
Custom *Customtemp = new Custom();
customList.append(Customtemp);
customList.at(customList.size()-1)->start();
Custom_sum = customList.size();
}
void CustomManager::delCustom()
{
if(!customList.isEmpty())
{
customList.at(customList.size()-1)->isRun = 0;
customList.at(customList.size()-1)->quit();
customList.at(customList.size()-1)->wait();
delete customList.at(customList.size()-1);
customList.removeLast();
}
Custom_sum = customList.size();
qDebug()<<" now CustomSum = "<<Custom_sum;
}
4.生产者线程
生产者线程是一直在运行的(run函数的循环不断),区别于消费者线程的创建和退出,生产者线程在修改商品数量之前是根据标志位和商品数量满的调节去决定每一次的循环里面的是否要增加商品数量。
主线程在构造函数里面就创建并start了生产者线程。
producer.h
#include "producer.h"
Producer::Producer()
{
isProduce = 1;
connect(this,SIGNAL(stopProduce()),this,SLOT(stopProduce_slot()));
}
void Producer::run()
{
for(int i = 0;i<10000;i++)
{
Mutex.lock();
if(isProduce)
{
if(StoreSum<StoreSum_Max)
{
StoreSum = StoreSum + Produce_speed;
}
else
{
emit stopProduce();
}
}
Mutex.unlock();
sleep(1);
}
}
void Producer::stopProduce_slot()
{
isProduce = 0;
}
5.主线程
主线程首先需要
- 完成UI的一些显示和控制,通过UI的信号和槽函数去改变一些全局变量比如调节生产的速度并且显示,添加或者减少消费者的数量,实时显示商品数量和库存的状态。
- 在构造函数里面创建并且start生产者线程。
- 在构造函数里面创建消费者管理类,并且根据UI的控件操作去调用消费者管理类里的创建和删除消费者线程。
mainwindow.ui
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "custom.h"
#include "producer.h"
#include "appdata.h"
#include "custommanager.h"
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
void timerEvent(QTimerEvent *event);
private:
Ui::MainWindow *ui;
int ShowInfoTime;
Producer *producer;
CustomManager *customManager;
signals:
void StartProduce();
void storeSum_lack();
public slots:
void updateInfo();
void stopProduceSlot();
void startProduceSlot();
void Test_btn_clicked_slot();
void speedUp_btn_clicked_slot();
void speedDown_btn_clicked_slot();
void addCustom_btn_clicked_slot();
void reduceCustom_btn_clicked_slot();
};
#endif
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
updateInfo();
producer = new Producer;
customManager = new CustomManager();
ShowInfoTime = startTimer(500);
producer->start();
connect(producer,SIGNAL(stopProduce()),this,SLOT(stopProduceSlot()));
connect(this,SIGNAL(storeSum_lack()),this,SLOT(startProduceSlot()));
connect(ui->Test_btn,SIGNAL(clicked()),this,SLOT(Test_btn_clicked_slot()));
connect(ui->speedUp_btn,SIGNAL(clicked()),this,SLOT(speedUp_btn_clicked_slot()));
connect(ui->speedDown_btn,SIGNAL(clicked()),this,SLOT(speedDown_btn_clicked_slot()));
connect(ui->addCustom_btn,SIGNAL(clicked()),this,SLOT(addCustom_btn_clicked_slot()));
connect(ui->reduceCustom_btm,SIGNAL(clicked()),this,SLOT(reduceCustom_btn_clicked_slot()));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::timerEvent(QTimerEvent *event)
{
if(event->timerId() == ShowInfoTime)
{
updateInfo();
}
}
void MainWindow::updateInfo()
{
ui->produce_speed_lb->setText(QString::number(Produce_speed));
ui->store_sum_lb->setText(QString::number(StoreSum));
if(StoreSum<=10)
{
emit storeSum_lack();
}
ui->custom_sum_lb->setText(QString::number(Custom_sum));
}
void MainWindow::stopProduceSlot()
{
ui->InfoShow_testBrowser->append("[主线程]库存满,停止生产...");
}
void MainWindow::startProduceSlot()
{
if(!producer->isProduce)
{
producer->isProduce = 1;
ui->InfoShow_testBrowser->append("[主线程]库存不足,开始生产...");
}
else
{
Produce_speed+=2;
ui->InfoShow_testBrowser->append("[主线程]生产速度太慢,加快生产...");
}
}
void MainWindow::Test_btn_clicked_slot()
{
}
void MainWindow::speedUp_btn_clicked_slot()
{
if(Produce_speed<Produce_speedMax)
{
Produce_speed++;
ui->produce_speed_lb->setText(QString::number(Produce_speed));
}
}
void MainWindow::speedDown_btn_clicked_slot()
{
if(Produce_speed>Produce_speedMin)
{
Produce_speed--;
ui->produce_speed_lb->setText(QString::number(Produce_speed));
}
}
void MainWindow::addCustom_btn_clicked_slot()
{
customManager->creatCustom();
ui->custom_sum_lb->setText(QString::number(Custom_sum));
}
void MainWindow::reduceCustom_btn_clicked_slot()
{
customManager->delCustom();
ui->custom_sum_lb->setText(QString::number(Custom_sum));
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
总结
- 完成这个demo后其实对很多地方还是一知半解,比如线程的退出是否严谨,线程的同步还可以使用QReadWriterLock、QWaitCondition或者是QSemaphore会更好一些,不确定里面的一些基类的注释是不是有误。
- 完整的代码文件在另一个帖子。有问题或者对代码里面的建议请联系我,谢谢。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)