const 成员函数中的互斥或原子
mutex or atomic in const member function
我正在阅读 Scott Meyers 的 Effective Modern C++ 中的第 16 条。
在项目的后半部分,他说
For a single variable or memory location requiring synchronization, use of a std::atomic
is adequate, but once you get to two or more variables or memory locations that require manipulation as a unit, you should reach for
a mutex.
但我仍然不明白为什么它在单个变量或内存位置的情况下就足够了,以本项中的多项式为例
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
if (!rootsAreValid) { // if cache not valid
.... // **very expensive compuation**, computing roots,
// store them in rootVals
rootsAreValid = true;
}
return rootVals;
}
private:
mutable std::atomic<bool> rootsAreValid{ false };
mutable RootsType rootVals{};
};
我的问题是:
如果线程 1 在 rootAreValid
分配给 true
之前正在大量计算 rootVals
,并且线程 2 也调用函数 roots()
,并且将 rootAreValid
评估为 false
,然后线程 2 也将进入 rootVals
的繁重计算,那么对于这种情况,原子 bool
如何足够?我仍然认为需要 std::lock_guard<mutex>
来保护 rootVals
计算的入口。
在您的示例中,有两个变量被同步:rootVals
和 rootsAreValid
。该特定项目指的是 只有 原子值需要同步的情况。例如:
#include <atomic>
class foo
{
public:
void work()
{
++times_called;
/* multiple threads call this to do work */
}
private:
// Counts the number of times work() was called
std::atomic<int> times_called{0};
};
times_called
是本例中唯一的变量。
我建议您使用以下代码避免不必要的繁重计算:
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
if (!rootsAreValid) { // Acquiring mutex usually is not cheap, so we check the state without locking
std::lock_guard<std::mutex> lock_guard(sync);
if (!rootsAreValid) // The state could changed because the mutex was not owned at the first check
{
.... // **very expensive compuation**, computing roots,
// store them in rootVals
}
rootsAreValid = true;
}
return rootVals;
}
private:
mutable std::mutex sync;
mutable std::atomic<bool> rootsAreValid{ false };
mutable RootsType rootVals{};
};
我正在阅读 Scott Meyers 的 Effective Modern C++ 中的第 16 条。 在项目的后半部分,他说
For a single variable or memory location requiring synchronization, use of a
std::atomic
is adequate, but once you get to two or more variables or memory locations that require manipulation as a unit, you should reach for a mutex.
但我仍然不明白为什么它在单个变量或内存位置的情况下就足够了,以本项中的多项式为例
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
if (!rootsAreValid) { // if cache not valid
.... // **very expensive compuation**, computing roots,
// store them in rootVals
rootsAreValid = true;
}
return rootVals;
}
private:
mutable std::atomic<bool> rootsAreValid{ false };
mutable RootsType rootVals{};
};
我的问题是:
如果线程 1 在 rootAreValid
分配给 true
之前正在大量计算 rootVals
,并且线程 2 也调用函数 roots()
,并且将 rootAreValid
评估为 false
,然后线程 2 也将进入 rootVals
的繁重计算,那么对于这种情况,原子 bool
如何足够?我仍然认为需要 std::lock_guard<mutex>
来保护 rootVals
计算的入口。
在您的示例中,有两个变量被同步:rootVals
和 rootsAreValid
。该特定项目指的是 只有 原子值需要同步的情况。例如:
#include <atomic>
class foo
{
public:
void work()
{
++times_called;
/* multiple threads call this to do work */
}
private:
// Counts the number of times work() was called
std::atomic<int> times_called{0};
};
times_called
是本例中唯一的变量。
我建议您使用以下代码避免不必要的繁重计算:
class Polynomial {
public:
using RootsType = std::vector<double>;
RootsType roots() const
{
if (!rootsAreValid) { // Acquiring mutex usually is not cheap, so we check the state without locking
std::lock_guard<std::mutex> lock_guard(sync);
if (!rootsAreValid) // The state could changed because the mutex was not owned at the first check
{
.... // **very expensive compuation**, computing roots,
// store them in rootVals
}
rootsAreValid = true;
}
return rootVals;
}
private:
mutable std::mutex sync;
mutable std::atomic<bool> rootsAreValid{ false };
mutable RootsType rootVals{};
};