You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程下std::unordered_map元素修改与插入的线程安全性问询

线程安全问题分析:unordered_map与多线程下的读写同步

问题描述

我有容器std::unordered_map<std::string, std::vector<int>> myMap;和互斥量std::mutex myMutex,初始访问myMap的代码如下:

void insert(const std::string& Key)
{
  std::lock_guard<std::mutex> lock(myMutex);
  if(!myMap.contains(Key))
  {
    auto& v = myMap[Key];
    // 对v做一些操作使其非空
  }
}
std::vector<int>& get(const std::string& Key)
{
  std::lock_guard<std::mutex> lock(myMutex);
  static std::vector<int> empty; // 此容器仅用于只读
  if(myMap.contains(Key))
  {
    auto& v = myMap[Key];
    return v;
  }
  return empty;
}

现在想确认:当一个线程执行如下代码修改容器(修改apples时不用myMutex,但用其他互斥量同步同key的写入),同时另一个线程调用insert("AnotherApple");,这种场景是否线程安全?

std::vector<int>& apples = get("Apple");
if(!apples.empty()) // 检查是否存在
{
  // 修改apples
}

我的场景中,insert和get已通过myMutex同步,但修改get返回的指定key引用值时,并未与insert/get同步。查阅资料后仍有疑问,补充完整代码如下:

class myManager
{
public:
    myManager() = default;
    void insert(const std::string& id)
    {
        std::lock_guard<std::mutex> lock(myMutex);
        if (!myMap.contains(id))
        {
            myMap[id];// 这里如果抛出异常,会不会有问题?

            try {
                mySeparateMutex[id];
            }
            catch (...)
            {
                myMap.erase(myMap.find(id));// 需要从myMap中删除id对应的元素,保证与mySeparateMutex一致
                throw;
            }
        }
    }
    std::tuple<std::vector<int>&, std::mutex&> get(const std::string& id, bool& ok)
    {
        std::lock_guard<std::mutex> lock(myMutex);
        static std::vector<int> Empty;
        static std::mutex noMutex;
        ok = false;
        if (myMap.contains(id))
        {
            ok = true;
            return std::tie(myMap[id], mySeparateMutex[id]);// 我认为operator[]不会抛出异常
        }
        return std::tie(Empty, noMutex);
    }
private:
    // 假设只能通过上面的公共成员函数直接访问这些私有成员
    std::mutex myMutex;
    std::unordered_map<std::string, std::vector<int>> myMap;
    std::unordered_map<std::string, std::mutex> mySeparateMutex;
};

myManager instance;

void fun0(const std::string& id)
{
    bool isContain = false;
    auto [a, b] = instance.get(id, isContain);
    if (isContain)
    {
        std::lock_guard<std::mutex> lock(b);// 此互斥量用于防止同key的容器读写冲突
        // 做一些修改
        a.push_back(1);
        a.push_back(2);
    }
}
void fun1(const std::string& id)
{
    instance.insert(id);
}
int fun2(const std::string& id, int index)
{
    bool isContain = false;
    auto [a, b] = instance.get(id, isContain);
    if (isContain)
    {
        std::lock_guard<std::mutex> lock(b);
        if (index >= 0 && index < a.size())
        {
            // 做一些读取操作
            return a[index];
        }
    }
    return -1;
}

int main()
{
    fun1("apple");
    std::thread a([]() {
        fun0("apple");
        });
    std::thread b([]() {
        fun1("apple");
        });
    std::thread c([]() {
        fun2("apple", 0);
        });
    std::thread d([]() {
        fun0("apple");
        });
    // 还有其他线程用"banana"等id调用fun*函数

    a.join();
    b.join();
    c.join();
    d.join();
    // 等待其他线程结束
    return 0;

}

结论:你的实现是线程安全的

核心安全逻辑说明

  • 全局互斥量myMutex的作用
    它保护的是myMap和mySeparateMutex两个哈希表的结构完整性:插入新键、查找键、删除键等操作都在myMutex保护下完成,避免多线程同时修改哈希表结构导致的未定义行为。
    当线程调用insert("AnotherApple")时,只会修改哈希表中与AnotherApple相关的条目,完全不会影响已存在的Apple对应的vector<int>和其专属互斥量,两者操作的是哈希表中独立的位置,无冲突风险。

  • 专属互斥量mySeparateMutex的作用
    每个key对应的vector<int>都有独立的互斥量,确保同一时间只有一个线程能修改或读取该vector,避免了同一vector的并发读写/写写冲突。
    从get函数获取vector引用后,所有操作都在对应专属互斥量的保护下,符合C++容器并发访问规则:只要不同线程操作容器中独立的元素,且元素自身同步做好,就是安全的。

补充细节验证

  • insert函数的异常安全:你在insert中的异常处理是正确的。如果mySeparateMutex[id]抛出异常(比如内存不足),会删除已插入myMap的对应id条目,保证myMap和mySeparateMutex的一致性,不会出现“有vector但无对应互斥量”的情况。
  • get函数的返回值安全:当key存在时,myMutex保证哈希表结构不会被修改,myMap[id]和mySeparateMutex[id]能稳定获取到引用,不会出现迭代器失效或引用悬空的问题。
  • 跨key操作的独立性:比如线程修改Apple的vector,同时另一个线程插入AnotherApple,两个操作完全不交叉,myMutex保护哈希表结构,专属互斥量保护vector,各司其职,无安全风险。

内容的提问来源于stack exchange,提问作者0x314159

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 23:24:52