【问题标题】:Safe use of a reference in a map in multi-threaded context在多线程上下文中安全使用映射中的引用
【发布时间】:2016-05-18 10:40:02
【问题描述】:

上下文

我有一个类(比方说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. 我的第一个问题是关于getData 返回的对Foo::data 的引用:这个引用超出了互斥锁的范围,那么它可能有问题吗?是否有可能由于另一个访问添加数据而丢失引用?简而言之:地图中的引用总是相同的吗?
  2. 如果是,assert 是否需要在 addData 中?如果我更改链接到地图中现有键的数据,参考可以更改吗?
  3. getData 中是否需要锁?如果std::map 已经是多线程安全的,我想可能不会。

【问题讨论】:

  • 您是在此处使用断言,因为您的程序逻辑不允许在添加数据之前调用getData,还是使用它来检查地图中是否存在数据?
  • @MohamadElghawi :这并不是真的不允许,但显然不是使用该对象的好方法。在我的程序中,Foo::data 是关于创建具有 uniqid 的对象的历史。尝试访问不存在的对象的历史是没有意义的。我使用assert,因为如果在发布模式下创建并返回空历史记录,这不是什么大问题。
  • 如果不被禁止,那么我建议使用断言以外的东西。断言通常不会编译到发布版本中,因此您的检查不会停止存在于生产环境中。
  • 这正是我所说的:在发布模式下不需要。对于使用该对象的开发人员来说,这是一个错误,而不是对于应用程序的用户。 (参见按合同设计的编程)
  • 其实我也有一个static bool hasKey(const std::string&amp; key)函数,供用户在调用之前检查key是否存在。用户有责任仅使用现有密钥调用 getData,并且他拥有执行此操作的工具。但是不需要在我的问题中公开此功能。

标签: c++ concurrency reference mutex


【解决方案1】:
  1. 是否可能由于另一个访问添加数据而导致引用丢失?简而言之:地图中的引用总是相同的吗?

Lifetime of references in STD collections

"对于std::map,只要不清除地图,不擦除特定引用元素,引用就有效;插入或擦除其他元素都可以。"

  1. 如果我更改链接到地图中现有键的数据,参考可以更改吗?

根据上述规则,不...修改映射中已有的引用只是调整同一地址的位。 (只要您不通过删除密钥然后重新添加该密钥来实现修改。)

  1. getData 中是否需要锁?如果 std::map 已经是多线程安全的,我认为可能不会。

C++11 STL containers and thread safety

所以锁是必需的,因为在addData() 的插入过程中,映射结构的“内部布线”可能会不断变化……在没有防护的情况下,在getData() 的查找遍历过程中,该布线可能会被触发.

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-19
    • 2014-04-12
    • 2019-07-08
    • 2011-03-14
    相关资源
    最近更新 更多