C++11 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(的一些演讲,特别是这张幻灯片:

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

这让我相信调用empty()是线程安全的。但是我的变量(_name(是mutable。"const == 线程安全"规则在这里仍然适用吗?

get_name()是唯一可以修改_name的函数,就此而言。

不,它不是线程安全的,因为您(读取(访问_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引用的线程都可以安全地从中读取。

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

最新更新