使用std::map时必须互斥的所有操作/函数是什么?

What are all the operations/functions that has to be Mutexed when using std::map

本文关键字:操作 函数 是什么 map std 使用      更新时间:2023-10-16

我有一个std::map,其中tuple作为键和值的向量的组合,如

using ReqList=map<const Attrib,vector<REQ>,less<key_comp>>;//属性是一个元组

对于这个映射,线程将查找键,如果键是新的,则插入;如果已经有键,则填充向量,然后进行某种形式的删除,如

ReqList l; begin(l[A])=move(l[A].back());//从值向量中移除第一个元素

我一直在做插入,查找,操作值,并删除(第一个元素)…那么,是否每个提到的操作或函数调用都需要被互斥?或者我只能把某些东西放在互斥锁下。map是否提供任何默认同步?

这其实很简单:如果你在"从map中读取"的同时"写入",那么你需要同步这些操作——例如用互斥锁保护它们。

这里的"写入到映射"是指调用映射的任何非const成员函数,而"从映射中读取"是指调用任何const成员函数。

同时时,我的意思是有多个线程可以写入或读取映射。因此,单线程程序不需要同步。

如果你在某个时间点写映射,然后绝对肯定地知道不会再写了,那么在那之后所有的读映射都可以不同步地进行。例如,如果您在一个线程的排序工厂函数中设置了一个映射,并且在设置之后再也不写入它,那么任何其他线程都可以不同步地从它读取。注意,当map超出作用域时,它的析构函数将运行,并将其写入;在此之前,所有可能的非同步读取必须已经完成。

如果你有一个线程从map读取数据,另一个线程向map写入数据,并且你不知道这些操作是如何相互同步的,互斥所有。保护对地图的每个访问。标准容器不提供保证,也不可能同步。

一般来说,作为经验法则,如果您对任何对象调用的所有方法都是const,则不需要围绕它进行同步。只要你对它做了一个非const操作,你就需要在每次触摸它的时候同步。

这不是万无一失的,这是一个经验法则,例如,一个函数可以声明自己为const,然后抛弃const,做一些危险的事情,或者它可以声明成员是可变的,所以即使是const,也让自己接触它。反过来也是正确的,你可以使用无锁编程技术来避免同步和保持一致性。

From [res.on.data.][/2]我们有

c++标准库函数不能直接或间接地修改当前线程以外的线程可以访问的对象(1.10),除非这些对象是通过函数的非const参数直接或间接访问的,包括this。

因此所有非const方法都允许修改状态,而所有const函数都不能修改对象的状态。

经验法则是,如果你的数据在多个线程之间共享,并且至少有一个线程是写线程,那么你需要同步。

template<class T>
struct rw_locked {
  template<class F>
  auto read( F&& f ) const {
    auto lock = read_lock();
    return std::forward<F>(f)(t);
  }
  auto write( F&& f ) {
    auto lock = write_lock();
    return std::forward<F>(f)(t);
  }
private:
  auto read_lock() const {
    return std::shared_lock<std::shared_timed_mutex>(m);
  }
  auto write_lock() {
    return std::unique_lock<std::shared_timed_mutex>(m);
  }
  mutable std::shared_timed_mutex m;
  T t;
};

现在只需:

rw_locked< std::map< int, int > > m;
m.read( [&](auto&&m) {
  // code to read
} );
m.write( [&](auto&&m) {
  // code to write
} );

,它将在大多数情况下正确保护。

从技术上讲,您可以在读锁中调用非const begin,并且它将被定义,但通过结果迭代器写入将不会被定义。所以上面的代码不能让你得到非常量迭代器。

不要在上面的代码中返回指向map中的数据的迭代器或指针,否则它不会保护你。