我有以下类(class):
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
)是可变的
。 “const == 线程安全”规则在这里仍然适用吗?
get_name()
是唯一可以修改 _name
的函数。
最佳答案
不,它不是线程安全的,因为您(读取)在 mutex
之外访问 _name
,这会破坏同步。
一个可能的解决方案是使用标准库提供的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()
不会被多次调用。第一次调用将初始化字符串
,并发调用将阻塞,直到 lambda 完成。
同步得到了充分处理,这意味着任何获取字符串
引用的线程都可以安全地从中读取。
关于multithreading - C++11 const - 我的代码是线程安全的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44527103/