C++ 在使用std::map时,哪些操作/函数必须被静音

C++ 在使用std::map时,哪些操作/函数必须被静音,c++,c++11,vector,stdmap,C++,C++11,Vector,Stdmap,我有一个std::map,其中元组作为键和值向量的组合,如 使用ReqList=map;//Attrib是一个元组 使用此映射,线程将查找密钥,如果密钥是新的,则执行插入操作;如果已经有密钥,则仅填充向量,然后执行某种形式的删除操作,如 需求清单1; beginl[A]=movel[A]。返回//从值向量中删除第一个元素 我一直在做插入、查找、操作值和删除第一个元素的工作。那么每个提到的操作或函数调用都需要被静音吗?或者我只能将某些东西置于互斥模式下。std::map是否提供任何默认同步?如果一

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

使用ReqList=map;//Attrib是一个元组

使用此映射,线程将查找密钥,如果密钥是新的,则执行插入操作;如果已经有密钥,则仅填充向量,然后执行某种形式的删除操作,如

需求清单1; beginl[A]=movel[A]。返回//从值向量中删除第一个元素


我一直在做插入、查找、操作值和删除第一个元素的工作。那么每个提到的操作或函数调用都需要被静音吗?或者我只能将某些东西置于互斥模式下。std::map是否提供任何默认同步?

如果一个线程从map读取,另一个线程向map写入,并且您不知道这些操作将如何相互同步,请将所有内容互斥。保护对地图的所有访问。标准容器不提供任何同步保证和可能性。

如果一个线程从映射中读取,另一个线程向映射中写入,并且您不知道这些操作将如何精确地相互同步,请将所有内容互斥。保护对地图的所有访问。标准容器不提供任何保证,也不可能进行同步。

这真的很容易:如果您在读取映射的同时写入映射,那么您需要同步这些操作,例如通过使用互斥锁来保护它们

对于写入映射,我的意思是调用映射的任何非常量成员函数;对于读取映射,我的意思是调用任何常量成员函数

同时,我的意思是有不止一个线程可以写或读地图。因此,单线程程序不需要同步

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;
};

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

这真的很容易:如果您在读取映射的同时写入映射,那么您需要同步这些操作-例如,通过使用互斥锁来保护它们

对于写入映射,我的意思是调用映射的任何非常量成员函数;对于读取映射,我的意思是调用任何常量成员函数

同时,我的意思是有不止一个线程可以写或读地图。因此,单线程程序不需要同步

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;
};

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

一般来说,根据经验法则,如果对任何对象调用的所有方法都是常量,则不需要围绕它进行同步。只要你在它上做了一件非常量的事情,你就需要在每次触摸它时同步


这不是防弹的,这是一条经验法则,例如,一个函数可以声明自己为常量,然后丢弃常量并执行危险的操作,或者它可以声明成员为可变的,因此即使当常量为常量时,也可以让它自己接触它。相反,也可以使用无锁编程技术来避免同步和保持一致性。

一般来说,根据经验法则,如果对任何对象调用的所有方法都是常量,则不需要围绕它进行同步。只要你在它上做了一件非常量的事情,你就需要在每次触摸它时同步


这不是防弹的,这是一条经验法则,例如,一个函数可以声明自己为常量,然后丢弃常量并执行危险的操作,或者它可以声明成员为可变的,因此即使当常量为常量时,也可以让它自己接触它。同样,相反的情况也是如此,您可以使用无锁编程技术来避免需要同步和保持一致性。

来自[res.on.data.races]/2

一个C++标准库函数不直接或间接地修改除了当前线程之外的线程可访问的对象1.10,除非对象直接或间接地通过函数访问 s的非常量参数,包括此参数

因此,所有非常量方法都可以修改状态,所有常量函数都不能修改对象的状态


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

来自[res.on.data.races]/2我们有

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;
};

一个C++标准库函数不直接或间接地修改对象1.10,而不是通过当前线程的线程访问,除非对象直接或间接地通过函数的非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
} );
它将在大部分情况下正确地进行防护

从技术上讲,您可以在读锁中调用non-const begin,它将被定义,但通过生成的迭代器进行写入将不会被定义。因此,上面不允许您获得非常量迭代器

在上面的代码中,不要返回迭代器或指向映射中数据的指针,否则不会保护您

现在只需做:

rw_locked< std::map< int, int > > m;
m.read( [&](auto&&m) {
  // code to read
} );
m.write( [&](auto&&m) {
  // code to write
} );
它将在大部分情况下正确地进行防护

从技术上讲,您可以在读锁中调用non-const begin,它将被定义,但通过生成的迭代器进行写入将不会被定义。因此,上面不允许您获得非常量迭代器


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

当线程共享映射时,读写操作需要得到保护。坦率地说,在这种情况下,我不会为下面有典型红黑树的std::map而烦恼。在MT环境中,如果您需要有序映射,请查看跳过列表。非常有名的是一些合理的实现:当线程共享映射时,读写操作需要得到保护。坦白地说,在这种情况下,我不会为下面有典型红黑树的std::map而烦恼。在MT环境中,如果您需要有序映射,请查看跳过列表。相当著名的一些合理的实现:begin不允许修改状态。begin不允许修改状态。@从床上摔下来不要忘记,你的一些地图活动可能会,我不知道你的情况;您可以完全避免这种情况,因为这需要多个步骤,并且允许在步骤之间进行访问可能会暴露不一致的程序状态。研究关键词:事务处理。@FallingFromBed别忘了你的一些地图活动可能,我不知道你的情况;您可以完全避免这种情况,因为这需要多个步骤,并且允许在步骤之间进行访问可能会暴露不一致的程序状态。研究关键词:事务处理。