线程安全std :: map:锁定整个地图和各个值[重复]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程安全std :: map:锁定整个地图和各个值[重复]相关的知识,希望对你有一定的参考价值。

这个问题在这里已有答案:

struct Data
{
 ...
 CRITICAL_SECTION valLock;
}    
std::map<int, Data> mp;
CRITICAL_SECTION mpLock;

我目前正在使用两个关键部分来使这个线程安全。

我必须锁定mapData更新Data

//Lock mpLock
//Lock mp[key1].valLock
mp[key1].something = something_new;
//unlock mp[key1].valLock
//unlock mpLock

我查看了intel的并发hashmap,它不需要两个锁并在内部处理它。如果我不想使用intel的tbb,还有其他方法。我只有c++ 98支持。虽然可以使用boost。看看boost::shared_mutex,但无法解释我如何在当前场景中使用它。

编辑:真的需要锁定容器吗?我不能使用Data::valLock来读/写Data。在mp中的任何插入都不会影响现有的迭代器,因此不需要锁定。任何从mp删除都将在Data::valLock之前。这里可能会错过哪些案例?

编辑2:

UpdateThread()
{
   //Lock mp[key].valLock
   mp[key].a = b;         //Line 1
   //unlock mp[key].valLock
}

ReadThread()
{
    //Lock mp[key].valLock
   something = mp[key].a;   //Line 2
   //unlock mp[key].valLock
}

所以我认为第2行只能在第1行完成时执行(反之亦然),即mp已经更新(以及地图内部)。那么它不安全吗?如果不是那么这意味着如果一个线程修改了mp [key1],而另一个线程读取了mp [key2],这是数据竞争?

答案

需要一个互斥锁才能使容器具有线程安全性。并且每个对象的互斥锁使这些对象中的每一个都是线程安全的。

每个对象的互斥体是次优设计。另一种设计是使用可复制但不可变的对象或在容器中存储共享/侵入指针。

使用不可变对象,读取器锁定容器(用于读取),制作元素的副本并解锁容器。作家锁定容器(用于写入)添加/删除/修改元素并解锁。因为读者总是复制元素,所以元素上的线程之间永远不会发生争用。

使用共享指针,读者可以执行上述操作。作者也如上所述,而不是修改现有元素,作家总是创建一个新元素并替换现有元素。

不可变对象的示例:

template<class Key, class Value>
class ThreadSafeMap
{
    std::mutex m_;
    std::map<Key, Value> c_;

public:
    Value get(Key const& k) {
        std::unique_lock<decltype(m_)> lock(m_);
        return c_[k]; // Return a copy.
    }

    template<class Value2>
    void set(Key const& k, Value2&& v) {
        std::unique_lock<decltype(m_)> lock(m_);
        c_[k] = std::forward<Value2>(v);
    }
};

您可能还想使用std::unordered_map(或开源代码)代替std::map来获得更好的性能。 std::map相当缓存不友好。

另一答案

真的需要锁定容器吗?

我不能使用Data :: valLock来读/写数据。

当然你可以,这已经是你正在使用它。

mp中的任何插入都不会影响现有的迭代器,因此不需要锁定

错误。

插入映射不会使现有迭代器无效,但如果您同时插入或删除两个线程(或者一个线程插入/删除和另一个线程查找),则不安全。这不是因为迭代器失效,而是因为更新和遍历内部节点指针图不能同时安全地完成。

任何其他基于节点的容器都可能是这样。

现在,如果在保持容器锁的同时获得(并保留)映射到地图的迭代器,只要迭代器没有失效,您就可以通过该迭代器继续引用该单个节点而不保持容器锁。这可以。

如果你想对除迭代之外的迭代器做任何事情,那么你需要再次使用容器锁。推进迭代器或找到另一个迭代器,两者都必须遍历节点图(如果你没有持有锁),可能会由另一个执行插入或删除的线程在你的下面进行变异。

以上是关于线程安全std :: map:锁定整个地图和各个值[重复]的主要内容,如果未能解决你的问题,请参考以下文章

锁定 std::map C++

如何以线程安全的方式使用`std::unordered_map`?

使用线程不安全的静态变量锁定嵌套函数

std::map 访问线程是不是安全,如果它的迭代器永远不会失效

最小化锁争用 c++ std::map

std::unordered_map 上的线程安全包装器