【发布时间】:2017-01-25 00:44:49
【问题描述】:
在第16条:“使const成员函数线程安全”中有一段代码如下:
class Widget {
public:
int magicValue() const
{
std::lock_guard<std::mutex> guard(m); // lock m
if (cacheValid) return cachedValue;
else {
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
} // unlock m
private:
mutable std::mutex m;
mutable int cachedValue; // no longer atomic
mutable bool cacheValid{ false }; // no longer atomic
};
我想知道为什么 std::lock_guard 应该总是在每个 magicValue() 调用上执行,不会像预期的那样工作?:
class Widget {
public:
int magicValue() const
{
if (cacheValid) return cachedValue;
else {
std::lock_guard<std::mutex> guard(m); // lock m
if (cacheValid) return cachedValue;
auto val1 = expensiveComputation1();
auto val2 = expensiveComputation2();
cachedValue = val1 + val2;
cacheValid = true;
return cachedValue;
}
} // unlock m
private:
mutable std::atomic<bool> cacheValid{false};
mutable std::mutex m;
mutable int cachedValue; // no longer atomic
};
这样需要更少的互斥锁,使代码更高效。我在这里假设 atomica 总是比互斥锁快。
[编辑]
为了完整起见,我测量了两个方法的效率,第二个看起来只快 6%。:http://coliru.stacked-crooked.com/a/e8ce9c3cfd3a4019
【问题讨论】:
-
因为读取cacheValid可能不是原子的?
-
@NeilButterworth:欢迎回来。
-
您明白,您的计算可以使用第二个版本多次运行
-
@steve 其实我没有,你能写出来回答为什么吗?我认为将 cacheValid 设置为 std::atomic
就足够了,还有一个 lock_guard 我认为应该防止双重计算。 -
你检查cacheValid是否为假,代码将切换到else情况,此时线程被中断。下一个线程检查 cacheValid 仍然为假,转到 else 情况.......
标签: c++ multithreading atomic