修改值是否必须使用互斥锁?修改unordered_map元素值是否要加锁?
关于unordered_map修改元素值是否需要加锁的解答
核心结论:你当前的写法是不安全的,修改元素值是否需要加锁,要同时考虑容器结构安全性和元素本身的并发访问安全性两个维度。
一、容器层面的安全特性
std::unordered_map 本身不是线程安全容器,只有以下规则成立:
- 多个线程同时只读容器(仅查找、遍历,不做增删改结构)是安全的
- 增、删、rehash等修改容器结构的操作,会导致迭代器失效,必须加互斥锁
- 如果仅修改已存在元素的value值,不会改变容器结构,也不会导致其他迭代器失效,这一步本身不会触发容器层面的并发问题
二、你当前写法的两处核心风险
1. 元素被其他线程删除导致迭代器失效
你在try_emplace拿到迭代器后就立刻释放了全局锁,此时如果有其他线程执行你已经加锁的删除操作,完全可以把你当前持有的迭代器对应的节点删掉,此时element就变成失效迭代器,后续访问element->second属于访问野内存,直接触发未定义行为。
2. 元素本身的并发访问数据竞争
哪怕你能保证该元素不会被其他线程删除,只要有多个线程同时读写同一个Connection对象的成员变量,就会触发数据竞争:你示例中修改connection.foo = "bar"的操作,如果有其他线程同时读/写foo字段,且foo不是原子类型也没有单独加锁,就是标准的未定义行为,可能出现读到半修改的脏数据、程序崩溃等问题。
三、可行的优化方案
- 只有当你能保证同一个key对应的
Connection同一时间最多只会被一个线程访问,且元素不会被意外删除时,你当前的写法才是安全的,但绝大多数业务场景都满足不了这个前提。 - 通用场景推荐优化方案:
- 将容器的value改为
std::shared_ptr<Connection>,这样你拿到shared_ptr后哪怕元素被从map中删除,你手里的智能指针仍然持有对象的所有权,不会出现野访问 - 给每个
Connection单独加互斥锁,访问、修改对象成员时持有对应对象的锁,避免全局锁粒度过大影响并发性能
- 将容器的value改为
内容的提问来源于stack exchange,提问作者jeffbRTC
相关产品推荐
相关产品推荐

