惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
M
MIT News - Artificial intelligence
MyScale Blog
MyScale Blog
博客园_首页
G
Google Developers Blog
博客园 - 【当耐特】
美团技术团队
博客园 - 聂微东
Stack Overflow Blog
Stack Overflow Blog
Vercel News
Vercel News
小众软件
小众软件
博客园 - 司徒正美
雷峰网
雷峰网
T
Tailwind CSS Blog
V
V2EX
博客园 - 三生石上(FineUI控件)
F
Fortinet All Blogs
罗磊的独立博客
量子位
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
A
About on SuperTechFans
Hugging Face - Blog
Hugging Face - Blog

博客园 - 左直拳

在centos上部署gitea服务器 我的2025 利用wvp-gb28181-pro国标级联实现上级观看下级摄像头 将c++程序部署到docker 安装linux时的存储设置 使用Mobile Atlas Creator 2.0获取离线地图 利用SRS拉取摄像头辅码流实现浏览器展示多路摄像头预览功能 自签名证书需要手动确认风险导致nginx转发无效问题 程序跑在docker容器里会有性能瓶颈吗? 前端vue3+后端spring boot导出数据 linux下变更mysql的数据文件目录 c++项目使用drogon框架提供Resful API在CPU ARM架构下崩溃问题 编写c++程序分别在x86和arm架构的ubuntu下访问CAN 接口设备 时区问题导致java程序查询无结果 ubuntu 20.04挂载固态硬盘 项目绩效域 c++中构造对象实例的两种方式及其返回值 c++中“&”符号代表引用还是取内存地址? 高级项目管理
c++程序示例:多线程下的实例计数器
左直拳 · 2025-08-15 · via 博客园 - 左直拳

我的一个c++项目,有个类,运行过程中会创建多个实例,每个实例独自一个线程。原本程序设置了一个静态、全局的计数器,所有实例共用,但我希望每个实例管理自己的计数器。最自然的想法,应该设置一个私有变量作为计数器,每个实例操作自己的这个私有变量。但是,实例有回调函数,回调函数中会进行计数,不好传递这些个变量到回调函数。或者说,要传递一个实例私有变量到回调函数,比较麻烦,我不想搞得那么复杂。

一种解决的办法还是设置静态、全局的计数器,但改为集合,每个实例使用一个集合元素。优点是比较简单。具体代码如下:

定义一个全局的集合,集合元素是一个结构。集合的key,是类中一个关键的ID值,是唯一的。每个实例在构造函数中,往该集合中添加一个元素。

之所以进行计数,并且每个实例计数独立,目的不仅仅是为了观察程序运行情况,更重要的是,用于判断是否与设备失联。如果失联就需要进行重连。

一、代码介绍

1、定义

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、构造

类的构造函数调用此方法

ConnBoxSIIM::ConnBoxSIIM(int master_id) {
  this->master_id = master_id;
  initRecivCount(master_id);
  。。。
}

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)向设备发送指令时进行发送计数

  auto it = g_recivCountMap.find(this->master_id);
  if (it != g_recivCountMap.end()) {
    it->second.send();
  }

2)在设备信息回调函数中进行接收计数

    auto it2 = g_recivCountMap.find(master_id);
    if (it2 != g_recivCountMap.end()) {
      it2->second.receive();
    }

4、统计

定期进行统计

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;
  }
}

二、注意的问题

开发过程中,有一个问题很让我疑惑。就是为什么最新接收量永远等于累计接收量。明明该方法里面有运算

  long getNewReceived() {//最新接收量
    long new_cnt = received_count - last_received_count;
    last_received_count = received_count;
    return new_cnt > 0 ? new_cnt : 0;
  }

后来AI告诉我,主要是我的调用方法有问题:

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就应该算是引用类型,修改的都是同一份。)

应该是

ReceiveCount &counter = it->second;
long newRecvNum = counter.getNewReceived();

好奇戒啊。