在 C++ 11 中与 std::atomic 同步

Synchronisation in C++ 11 with std::atomic

我有以下代码在 Intel 处理器上运行良好但在 ARM 处理器上产生奇怪的数据。
我怀疑这是一个同步问题。

基本上我有一个生产者线程定期调用 setLiveData(...) 和一个消费者线程也定期调用 getLiveData(...)

.h 文件

class DataHandler{
public:
...
private:

    LiveDataValue lastValues_;
    bool lastValuesValid;
};

.cpp 文件

bool DataHandler::getLiveData(LiveDataValue *val)
{
    if(this->lastValuesValid){
        *val = this->lastValues_;
        return true;
    }else
        return false;
}

void DataHandler::setLiveData(LiveDataValue val)
{
    this->lastValuesValid = false;
    this->lastValues = val;
    this->lastValuesValid = true;
}

仅通过阅读代码,我认为我需要确保 setLiveData 是原子的,因为当生产者线程处于setLiveData(...)

中间

我找到了 并尝试用它修复代码:

.h 文件

class DataHandler{
public:
...
private:

    LiveDataValue lastValues_;
    std::atomic<bool> lastValuesValid;
};

.cpp 文件

bool DataHandler::getLiveData(LiveDataValue *val)
{
   while (!this->lastValuesValid.load(std::memory_order_acquire))
   {
       std::this_thread::yield();
   }

    if(this->lastValuesValid){
        *val = this->lastValues_;
        return true;
    }else
        return false;
}

void DataHandler::setLiveData(LiveDataValue val)
{
    this->lastValuesValid_.store(false, std::memory_order_release);
    this->lastValues = val;
    this->lastValuesValid_.store(true, std::memory_order_release);
}

我的问题是我从未退出 reader 线程调用的 getLiveData 中的 while 循环。这是为什么?

编辑:LiveDataValue 是一个复杂的联合 typedef,此处不详述。

你的问题是你的代码没有同步,而不是你的循环没有结束。

if(this->lastValuesValid){
    *val = this->lastValues_;
    return true;
}else
    return false;

您可以检查最后一个值是否有效,为真,并且在您分配它们时它们无效。之后不会立即进行任何有效性检查,它只是告诉您在过去的某个时间点它们是有效的。

template<class T>
struct mutex_guarded {
  template<class F>
  void read( F&& f ) const {
    auto l = std::unique_lock<std::mutex>(m);
    f(t);
  }
  template<class F>
  void write( F&& f ) {
    auto l = std::unique_lock<std::mutex>(m);
    f(t);
  }
private:
  mutable std::mutex m;
  T t;
};

这是一个简单的包装器,用于序列化对任意类型的某些数据的访问。

class DataHandler{
public:
...
private:

  struct Data {
    LiveDataHolder lastValues_;
    bool lastValuesValid_ = false;
  };
  mutex_guarded<Data> data_;
};

然后

bool DataHandler::getLiveData(LiveDataValue *val) const
{
  bool bRet = false;
  data_.read([&](Data const& data_){
    bRet = data_.lastValuesValid_;
    if (!bRet) return;
    *val = data_.lastValues;
  });
  return bRet;
}

void DataHandler::setLiveData(LiveDataValue val)
{
  data_.write([&](Data & data_){
    data_.lastValues = std::move(val);
    data_.lastValuesValid = true;
  });
}

将自动修改有效字段和值字段。

.read(lambda).write(lambda) 中完成的所有事情都是在锁定互斥量的情况下完成的。 lambda 传递 T const&T& 取决于它是读操作还是写操作,并且没有其他方法可以访问受保护的数据。

(扩展它以支持 reader/writer 锁相对容易,但保持简单是一个很好的经验法则,所以我只是用互斥锁编写它)