常量成员函数中的互斥或原子 我正在阅读Scott Meyers的现代C++中的第16项。 在项目的后面部分,他说

常量成员函数中的互斥或原子 我正在阅读Scott Meyers的现代C++中的第16项。 在项目的后面部分,他说,c++,multithreading,c++11,atomic,C++,Multithreading,C++11,Atomic,对于需要同步的单个变量或内存位置,使用std::atomic就足够了,但一旦您找到两个或多个需要作为一个单元进行操作的变量或内存位置,您应该 互斥锁 但我仍然不明白为什么它在单个变量或内存位置的情况下是足够的,以本项中的多项式为例 class Polynomial { public: using RootsType = std::vector<double>; RootsType roots() const { if (!rootsAreValid) { // if c

对于需要同步的单个变量或内存位置,使用
std::atomic
就足够了,但一旦您找到两个或多个需要作为一个单元进行操作的变量或内存位置,您应该 互斥锁

但我仍然不明白为什么它在单个变量或内存位置的情况下是足够的,以本项中的多项式为例

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{};
};
类多项式{
公众:
使用RootsType=std::vector;
RootsType根()常量
{
if(!rootsAreValid){//if缓存无效
..//**非常昂贵的计算**,计算根,
//将它们存储在rootVals中
rootsAreValid=true;
}
返回rootVals;
}
私人:
可变std::原子根serevalid{false};
可变RootsType rootVals{};
};
我的问题是:


如果线程1处于<>代码> RoToVals<代码>之前,RooTaRealeAuth/<代码>被分配给<代码> true,线程2也调用函数<代码> ROOTSH()/CODE >,并评估<代码> RooTaaReals > <代码> false <代码>然后线程2还将逐步进行大量的

rootVals
计算,那么对于这种情况,原子
bool
如何足够呢?我仍然认为需要一个
std::lock\u-guard
来保护
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};
};
#包括
福班
{
公众:
无效工作()
{
++时代呼唤;
/*多线程调用它来完成工作*/
}
私人:
//统计调用work()的次数
std::称为{0}的原子时间;
};

times\u called
是本例中唯一的变量。

在您的示例中,有两个变量正在同步:
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};
};
#包括
福班
{
公众:
无效工作()
{
++时代呼唤;
/*多线程调用它来完成工作*/
}
私人:
//统计调用work()的次数
std::称为{0}的原子时间;
};

times\u 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{};
};
类多项式{
公众:
使用RootsType=std::vector;
RootsType根()常量
{
如果(!rootsAreValid){//获取互斥通常不便宜,那么我们在不锁定的情况下检查状态
标准:锁紧保护锁紧保护(同步);
if(!rootsAreValid)//状态可能会更改,因为第一次检查时互斥锁不归其所有
{
..//**非常昂贵的计算**,计算根,
//将它们存储在rootVals中
}
rootsAreValid=true;
}
返回rootVals;
}
私人:
可变std::互斥同步;
可变std::原子根serevalid{false};
可变RootsType rootVals{};
};

我建议您使用以下代码避免不必要的繁重计算:

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{};
};
类多项式{
公众:
使用RootsType=std::vector;
RootsType根()常量
{
如果(!rootsAreValid){//获取互斥通常不便宜,那么我们在不锁定的情况下检查状态
标准:锁紧保护锁紧保护(同步);
if(!rootsAreValid)//状态可能会更改,因为第一次检查时互斥锁不归其所有
{
..//**非常昂贵的计算**,计算根,
//将它们存储在rootVals中
}
rootsAreValid=true;
}
返回rootVals;
}
私人:
可变std::互斥同步;
可变std::原子根serevalid{false};
可变RootsType rootVals{};
};

这显然是不够的。引文中说“对于需要同步的单个变量或内存位置……”@juanchopanza,这不是一个“对于需要同步的单个变量或内存位置”的例子吗,你也需要同步向量。实际上,在这种情况下,你需要另一个标志来检查是否有线程正在运行昂贵的计算。这是书中的例子还是你自己的场景?@Allankunzi,这很有道理。但是,您将代码表示为直接从书中获得,而不是经过修改。显然,这是不够的。引文中说“对于需要同步的单个变量或内存位置……”@juanchopanza,这不是一个“对于需要同步的单个变量或内存位置”的例子吗,你也需要同步向量。实际上,在这种情况下,你需要另一个标志来检查是否有线程正在运行昂贵的计算。这是书中的例子还是你自己的场景?@Allankunzi,这很有道理。但是,您将代码表示为直接来自书本而不是修改。我知道,谢谢,我没有意识到有两个变量正在同步。我知道,谢谢,我没有意识到有两个变量正在同步。检查
rootsAreValid
(常规bool)是不受保护的(在互斥锁之外)这是一个典型的数据竞争示例,因此无效。问题扩展到向量,该向量在与写入向量的线程不同步时返回。。。这将导致未定义的错误behavior@LWimsey,不存在数据竞争。值
rootsAreValid
不能从
true
更改为
false
。当
rootsAreValid
为false但这部分代码由互斥锁保护并进行额外比较时,如果多个线程中的两个试图计算向量,则可能会发生数据争用。第一次检查未担保的原因是性能