在多线程上下文中安全地使用地图中的引用

时间:2016-05-18 10:40:03

标签: c++ concurrency reference mutex

上下文

我有一个类(让我们说Foo)管理一些集中式资源作为静态地图中的历史记录,有一个读取它们的访问器和一个添加新数据的函数(没有办法删除一个键):

class Foo
{
  private:
    static std::map<std::string,MyDataStructure> data;
  public:
    static const MyDataStructure& getData(const std::string& key)
    {
      assert(Foo::data.count(key) > 0); // Must exist
      return Foo::data[key];
    }
    static void addData(const std::string& key, const MyDataStructure& d)
    {
      assert(Foo::data.count(key) == 0); // Can not already exist
      Foo::data[key] = d;
    }
};

为了避免并发问题,我添加了一个我这样管理的互斥锁:

class Foo
{
  private:
    static std::map<std::string,MyDataStructure> data;
    static boost::mutex mutex_data;
  public:
    static const MyDataStructure& getData(const std::string& key)
    {
      boost::mutex::scoped_lock lock(Foo::mutex_data);
      assert(Foo::data.count(key) > 0); // Must exist
      return Foo::data[key];
    }
    static void addData(const std::string& key, const MyDataStructure& d)
    {
      boost::mutex::scoped_lock lock(Foo::mutex_data);
      assert(Foo::data.count(key) == 0); // Can not already exist
      Foo::data[key] = d;
    }
};

我的问题

  1. 我的第一个问题是关于Foo::data返回的getData的引用:此引用是在互斥锁范围之外使用的,那么它是否可能出现问题?由于另一个访问添加数据,是否可能丢失引用?简而言之:地图中的引用始终是一样的吗?
  2. 如果是,assert需要addData吗?如果我更改链接到地图中现有密钥的数据,参考会更改吗?
  3. getData是否需要锁定?我想也许不是std::map已经是多线程安全的。

1 个答案:

答案 0 :(得分:2)

  1. 由于其他访问添加数据,是否可能会丢失引用?简而言之:地图中的引用始终是一样的吗?
  2. 请参阅Lifetime of references in STD collections

      

    “对于std::map,只要您不清除地图,或者删除特定的引用元素,引用就是有效的;插入或删除其他元素就没问题。”

    1. 如果更改链接到地图中现有密钥的数据,引用会更改吗?
    2. 根据上面的规则,不...修改地图中已有的引用只是调整同一地址的位。 (只要您不通过删除密钥实施修改,然后再次重新添加该密钥。)

      1. getData中是否需要锁定?我想也许不是因为std :: map已经是多线程安全的。
      2. 请参阅C++11 STL containers and thread safety

        因此锁定是必需的,因为在addData()插入期间,地图结构的“内部布线”可能会处于不稳定状态......在{{1}的查找遍历期间,布线可能会被触发没有警卫。

        但是,如果您手头有参考,则可以在添加或删除期间阅读或编写已存在的getData()引用之一。这只是从地图到参考导航的过程,需要确保在导航过程中没有人在写。