分布式协议中向unordered_map的vector值push_back异常问题
套接字分布式协议unordered_map写入异常Bug修复方案
1. 消息去重校验逻辑错误(仅打印1次的直接原因)
你当前的重复消息判定规则为「(recv_v, recv_s)键不存在才处理消息」,完全不符合业务逻辑:
- 同一视图
v、同一序列s下,你需要接收4个不同副本发来的同类型消息,第一次收到任意副本的消息时,push_sequence_signshare方法会创建该键对应的空vector并压入第一个副本ID,此时键已经存在于map中 - 后续3个副本发来相同
(v,s)的消息时,校验逻辑检测到键已存在,直接将消息判定为重复丢弃,根本不会执行后续的push、打印、阈值判断逻辑,自然只会打印1次日志。
正确的去重校验必须放在锁保护范围内执行:查找(recv_v, recv_s)对应的vector后,检查vector中是否已存在当前发送方的recv_repID,不存在才执行push_back,存在才判定为重复消息丢弃。
2. 自定义pair_hash实现存在严重缺陷,是程序异常返回的核心隐患
你采用两个元素哈希值直接异或的哈希实现冲突率极高,会直接导致unordered_map行为异常:
- 对任意值
x,pair(x,x)的哈希值永远为hash(x) ^ hash(x) = 0,所有两个元素相等的pair都会发生哈希碰撞 - 对任意值
a、b,pair(a,b)和(b,a)的哈希值完全相等,会产生大量无意义碰撞 - 过高的哈希冲突会让unordered_map的查找、插入性能从O(1)暴跌到O(n),极端情况下会触发桶内链表溢出、迭代器失效、内存踩踏,直接导致程序异常返回。
替换为分布更均匀的哈希实现即可解决该问题,参考代码:
struct pair_hash { template <typename T1, typename T2> std::size_t operator()(const std::pair<T1, T2>& p) const { std::size_t h1 = std::hash<T1>{}(p.first); std::size_t h2 = std::hash<T2>{}(p.second); // 移位混合代替纯异或,大幅降低哈希冲突率 return h1 ^ (h2 << 1); } };
3. 互斥锁覆盖范围不全,存在多线程竞态
你仅在push_sequence_signshare方法内部加了互斥锁,但前置的消息校验、map读取、后置的阈值判断逻辑都在锁外执行:
- 多线程并发读写unordered_map属于未定义行为,随时可能触发程序崩溃、数据错乱
- 校验逻辑和写入逻辑不在同一临界区,会触发TOCTOU(检查时间-使用时间)竞态:两个线程同时校验发现某副本ID不在vector中,同时执行push_back,导致重复插入数据。
修复方式:所有读写accepted_sequence_signshare的操作,包括重复校验、键查找、元素插入、vector长度读取做阈值判断,全部放到mtx_signshare锁的保护范围内,不要在锁外持有任何指向map内元素的指针、迭代器。
内容的提问来源于stack exchange,提问作者Impossible-Tennis
相关产品推荐
相关产品推荐

