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 计算的入口。

在您的示例中,有两个变量被同步:rootValsrootsAreValid。该特定项目指的是 只有 原子值需要同步的情况。例如:

#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{};
};