在仿真引擎中设计过程中比较重要的内容有时间同步控制、业务数据的流转处理。
1.时间同步
专门启动一个线程用来计时,采用C++11高精度时间库
std::chrono::high_resolution_clock::now()
详见之前的文章C++中高精度时间的记录控制
在时间线程中,处理线程中不能再进行任何其他的操作,否则会影响时间的精度。
当时间间隔到达规定的时间间隔时(10ms),就会触发相应的事件,这个的触发事件就是设置变量值(每次定时器开始的时候,设置变量为false,当时间间隔到的时候设置变量为true,在变量从false到true的过程中所运行的时间就是10ms,设置完成true之后,会再次设置成false,进入下一个时间间隔循环)。
其他需要时间的线程当读取到这个变量为true时,开始执行线程中的其他代码工作(更新界面线程开始更新界面数据、生成者线程开始生产数据等)。其他的线程是如何读取到这个变量值发生了变化了呢?除了时间线程以外,其他的线程都是实时的工作状态,也就是一个“死循环”,在循环中不停的检测这个变量的值是否变为true,一旦检测到这个变量发生了变化,就会立马执行。执行完成之后,会再次等待变量变成true。
2.数据流转
数据流转模型采用的是【生产者-消费者】模型,具体模型的含义大家可以自行查找资料,这方面的资料很多。简单来说就是实体产生数据,把产生的数据放到数据队列中,消费者从数据队列中获取到数据进行出来,处理完成之后,数据队列就会删除这条数据。
单独启动一个数据队列的维护线程,这个线程的主要目的就是删除已经被处理过的数据。
3.实例
数据队列管理:
#pragma once
#include <queue>
#include <QMutex>
class ItemData;
//缓冲数据管理
#define CacheDataMgr() (CacheDataManager::getInstance())
class CacheDataManager {
private:
CacheDataManager();
static CacheDataManager* _instance;
public:
~CacheDataManager();
static CacheDataManager* getInstance();
void clearInvalidData();
void addItemData(ItemData* data);
int dataCount();
void setChange(bool b) {
_isChange = b;
}
bool&getChange() {
return _isChange;
}
ItemData* getFrontData();
private:
std::queue<ItemData*> _cacheDataList;//数据队列
QMutex _mutex;//写入锁
bool _isChange = false; //数据队列是否发生变化
};
数据队列管理线程:
#include <QDebug>
#include "DataThread.h"
#include "CacheDataManager.h"
DataThread::DataThread(QObject *parent)
: QThread(parent) {
}
DataThread::~DataThread() {
}
void DataThread::run() {
while (true) {
usleep(1);
//1.数据列表发生了变化
if (CacheDataMgr()->getChange()) {
qDebug() << QStringLiteral("数据队列数量:%1").arg(CacheDataMgr()->dataCount());
CacheDataMgr()->setChange(false);
}
//2.清除掉无用的数据
CacheDataMgr()->clearInvalidData();
}
}
生成者线程:
#include <QRandomGenerator>
#include <QDebug>
#include "ProductThread.h"
#include "ItemDataString.h"
#include "ItemDataInt.h"
#include "CacheDataManager.h"
ProductThread::ProductThread( QObject *parent)
: QThread(parent) {
}
ProductThread::~ProductThread() {
}
void ProductThread::run() {
while (true){
msleep(100);
int randNum = QRandomGenerator::global()->bounded(2);//生成一个0和2之间的整数
ItemData* itemData = nullptr;
if (randNum%2 == 0) {//如果是偶数
itemData = new ItemDataInt;
qDebug() << QStringLiteral("线程ID:%1 产生整型数据").arg(QString::number(unsigned int(QThread::currentThreadId())));
} else {//奇数
itemData = new ItemDataString;
qDebug() << QStringLiteral("线程ID:%1 产生字符串数据").arg(QString::number(unsigned int(QThread::currentThreadId())));
}
//保存到缓存数据列表中
CacheDataMgr()->addItemData(itemData);
}
}
消费者线程:
#include <QDebug>
#include "ConsumeThread.h"
#include "CacheData/CacheDataManager.h"
#include "CacheData/ItemData.h"
ConsumeThread::ConsumeThread(QObject *parent)
: QThread(parent) {
}
ConsumeThread::~ConsumeThread() {
}
void ConsumeThread::run() {
while (true) {
usleep(1);
ItemData* frontData = CacheDataMgr()->getFrontData();
if (frontData == nullptr){
continue;
}
if (frontData->getValid()){
//获取队首数据,然后处理数据
qDebug() << QStringLiteral("消费者处理在时间%1产生的数据").arg(frontData->getDataProductTime());
//处理完成之后,数据就变得无效
frontData->setValid(false);
}
}
}
运行程序,输出结果:
"数据队列数量:1"
"消费者处理在时间2021-10-10 17:14:19.972产生的数据"
"数据队列数量:0"
"线程ID:34912 产生整型数据"
"线程ID:25076 产生整型数据"
"线程ID:1108 产生整型数据"
"数据队列数量:3"
"消费者处理在时间2021-10-10 17:14:20.082产生的数据"
"消费者处理在时间2021-10-10 17:14:20.082产生的数据"
"数据队列数量:2"
"数据队列数量:1"
"消费者处理在时间2021-10-10 17:14:20.082产生的数据"
"数据队列数量:0"
"线程ID:1108 产生整型数据"
"线程ID:34912 产生整型数据"
"线程ID:25076 产生字符串数据"
"数据队列数量:3"
"消费者处理在时间2021-10-10 17:14:20.190产生的数据"
"消费者处理在时间2021-10-10 17:14:20.190产生的数据"
"数据队列数量:2"
"消费者处理在时间2021-10-10 17:14:20.190产生的数据"
"数据队列数量:1"
"数据队列数量:0"
"线程ID:34912 产生整型数据"
"线程ID:25076 产生字符串数据"
"线程ID:1108 产生字符串数据"
"数据队列数量:3"
"消费者处理在时间2021-10-10 17:14:20.299产生的数据"
"数据队列数量:2"
"消费者处理在时间2021-10-10 17:14:20.299产生的数据"
"消费者处理在时间2021-10-10 17:14:20.299产生的数据"
"数据队列数量:1"
"数据队列数量:0"
"线程ID:1108 产生字符串数据"
"线程ID:34912 产生整型数据"
"线程ID:25076 产生字符串数据"
"数据队列数量:3"
"消费者处理在时间2021-10-10 17:14:20.408产生的数据"
"消费者处理在时间2021-10-10 17:14:20.408产生的数据"