我的一个c++项目,有个类,运行过程中会创建多个实例,每个实例独自一个线程。原本程序设置了一个静态、全局的计数器,实际上是所有实例共用,但我希望每个实例管理自己的计数器。最自然的想法,应该设置一个私有变量作为计数器,每个实例操作这个私有变量。但是,程序中有回调函数,不好传递这些个变量。
解决的办法还是设置静态、全局的计数器,但改为集合,每个实例对应一个集合元素。具体代码如下:
定义一个全局的集合,集合元素是一个结构。集合的key,是实例中一个关键的ID值,是唯一的。在实例的构造函数中,即往该集合中添加一个元素。进行计数的目的,不仅仅是为了观察程序运行情况,更重要的是,用于判断是否与设备失联。
一、代码介绍
1、定义
cpp
struct ReceiveCount { // 接收计数器
long sent_count = 0;//发送计数器
long received_count = 0;//接收计数器
long last_received_count = 0;//最上一次接收量
void reset() {//重置计数器
sent_count = 0;
received_count = 0;
last_received_count = 0;
}
void send() { sent_count++; }//发送计数
void receive() { received_count++; }//接收计数
float getLossRate() {//计算接收/发送比率
if (sent_count == 0)
return 0.0f;
return ((float)(sent_count - received_count) / sent_count) * 100.0f;
}
long getNewReceived() {//最新接收量
long new_cnt = received_count - last_received_count;
last_received_count = received_count;
return new_cnt > 0 ? new_cnt : 0;
}
long getReceived() { return received_count; }
long getSend() { return sent_count; }
};
std::map<int, ReceiveCount> g_recivCountMap; // key的类型是int
2、构造
类的构造函数调用此方法
cpp
void initRecivCount(int master_id) {
reciv_count_mtx.lock();
auto it = g_recivCountMap.find(master_id);
if (it == g_recivCountMap.end()) {
// 不存在:插入新元素(自动调用 ReciveCount 的默认构造)
g_recivCountMap.emplace(master_id, ReceiveCount{});
} else {
// 存在:重置计数器
it->second.reset();
}
reciv_count_mtx.unlock();
}
3、计数
1)向设备发送指令时进行发送计数
cpp
auto it = g_recivCountMap.find(this->master_id);
if (it != g_recivCountMap.end()) {
it->second.send();
}
2)在设备信息回调函数中进行接收计数
cpp
auto it2 = g_recivCountMap.find(master_id);
if (it2 != g_recivCountMap.end()) {
it2->second.receive();
}
4、统计
定期进行统计
cpp
auto it = g_recivCountMap.find(this_->master_id);
if (it != g_recivCountMap.end()) {
ReceiveCount &counter = it->second;
long newRecvNum = counter.getNewReceived();
printf("%s : loss data rate: %.2f%%,recive num:(%ld)\n", this_->siimip, counter.getLossRate(), newRecvNum);
if (newRecvNum == 0) {
no_new++;
if (no_new > 10) { // 连续10次没有收到回调消息,即认为已经断连
no_new = 0;
//此次进行重连。。。
}
} else {
no_new = 0;
}
}
二、注意的问题
开发过程中,有一个问题很让我疑惑。就是为什么最新接收量永远等于累计接收量。明明该方法里面有运算
cpp
long getNewReceived() {//最新接收量
long new_cnt = received_count - last_received_count;
last_received_count = received_count;
return new_cnt > 0 ? new_cnt : 0;
}
后来AI告诉我,主要是我的调用方法有问题:
cpp
ReceiveCount counter = it->second;
long newRecvNum = counter.getNewReceived();
原因是ReceiveCount counter = it->second; 相当于把 g_recivCountMap 中的 ReceiveCount 对象拷贝了一份,在 counter.getNewReceived() 中修改的是 拷贝体的 last_received_count,而 原始 map 中的对象没有更新!(这就跟c#和java的不一样。c#和java的话,couter就应该算是引用类型,修改的都是同一份。)
应该是
cpp
ReceiveCount &counter = it->second;
long newRecvNum = counter.getNewReceived();
好奇戒啊。