多线程操作unordered_map时,未加读锁的find()是否安全?
多线程下std::unordered_map的find()方法线程安全性问题
问题背景
多线程操作std::unordered_map时,其find()方法是否安全?现有如下C++代码:
uint16_t GetLevel(const string& tag) { static auto *tagMap = new std::unordered_map<std::string, int>(); static shared_timed_mutex* mtx = new shared_timed_mutex; std::decay<decltype(*tagMap)>::type::iterator it; { //ReadLock lock(*mtx); it = tagMap->find(tag); } if (it == tagMap->end()) { // new tag InsertLock lock(*mtx); it = tagMap->find(tag); // secured for two thread went across above condition if (it == tagMap->end()) { auto result = tagMap->insert({ tag, 1 }); if (!result.second) { return 0; } it = result.first; } } return it->second; }
具体问题
若tagMap->find(tag)前未加readLock,当其他线程对tagMap执行insert(...)操作时,该find()调用是否安全?
回答
绝对不安全,原因如下:
- C++标准明确规定,
std::unordered_map不是线程安全容器。当存在线程执行写操作(比如insert、erase、触发rehash的操作)时,其他线程同时执行读操作(包括find)会触发数据竞争,属于未定义行为。 insert操作可能触发容器的rehash——此时哈希表的内部结构会被彻底重构,包括链表节点的重组、内存的重新分配。这时候正在执行find的线程可能访问到失效的指针/迭代器,读到错误数据,甚至直接导致程序崩溃。- 你代码里的“双重检查”逻辑不成立:第一次无锁
find本身已经处于危险的竞争状态,就算后续加锁处理插入,也无法挽回前面无锁操作可能引发的未定义行为。
修正建议
- 必须给所有读操作(包括第一次
find)加上共享读锁(对应代码里的ReadLock,本质是std::shared_lock<std::shared_timed_mutex>)。这样多个读线程可以并行访问,写线程则需要等待所有读锁释放后才能获取独占锁执行insert,严格保证读写互斥。 - 保留第二次加锁后的
find检查是必要的,这是为了处理多个线程同时通过第一次无锁find(未找到)的情况,避免重复插入相同元素。
内容的提问来源于stack exchange,提问作者bu haha
相关产品推荐
相关产品推荐

