C ++ 11 const - 我的代码是否是线程安全的?

时间:2017-06-13 16:36:31

标签: multithreading c++11 thread-safety const

我有以下课程:

class Object 
{
public:
  Object() {}

  const std::string& get_name() const
  {
    if(_name.empty()) {
      std::lock_guard<std::mutex> lock(_lock);

      // Check if its still empty. Some other thread might have gotten here first
      if(_name.empty()) {
        //Run expensive operation
        _name = get_object_name();
      }
    }

    return _name;
  }

private:
  std::string get_object_name(); // <- Expensive function

  mutable std::mutex  _lock;
  mutable std::string _name;
};

由于get_object_name是一个昂贵的函数,我想进行一种惰性初始化,并且只在第一次调用get_name()时调用它。如果它从未被调用过那么我就不会浪费资源来抓住这个名字。

我担心第一次打电话给_name.empty()。我当前的代码是保证是线程安全的还是我需要将锁移动到函数的顶部?

我看了一些Herb Sutter的谈话,特别是this one,这张幻灯片出现了:

http://i.imgur.com/Jz4luYe.png

让我相信对empty()的调用是线程安全的。但我的变量(_name)是mutable。这个“const ==线程安全”规则是否仍适用于此?

get_name()是唯一可以修改_name的函数。

2 个答案:

答案 0 :(得分:3)

不,它不是线程安全的,因为您(读取)访问_name之外的mutex,这会破坏同步。

可能的解决方案是使用标准库提供的std::call_once机制。

class Object
{
public:
  Object() {}

  const std::string& get_name() const
  {
    std::call_once(flag, [&] { _name = get_object_name(); });

    return _name;
  }

private:
  std::string get_object_name() const; // <- Expensive function

  mutable std::string _name;
  mutable std::once_flag flag;
};

这可以保证get_object_name()不会被多次调用。第一个调用将初始化string,并且并发调用将阻塞,直到lambda完成 完全处理同步,这意味着任何获取string引用的线程都可以安全地从中读取。

答案 1 :(得分:0)

启动C ++ 11 static变量以线程安全方式初始化。 如果获得名称的昂贵操作static我认为以下更好:

class Object
{
public:
    const std::string& get_name() const
    {
        static std::string name = expensive_get_name();
        return name;
    }
private:
    static std::string expensive_get_name()
    {
        return "Name";
    }
};