Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/eclipse/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ C++;11原子内存排序-这是松弛(释放-消耗)排序的正确用法吗?_C++_Memory_C++11_Atomic_Compare And Swap - Fatal编程技术网

C++ C++;11原子内存排序-这是松弛(释放-消耗)排序的正确用法吗?

C++ C++;11原子内存排序-这是松弛(释放-消耗)排序的正确用法吗?,c++,memory,c++11,atomic,compare-and-swap,C++,Memory,C++11,Atomic,Compare And Swap,我最近使用三重缓冲区的std::atomic作为并发同步机制,创建了一个到C++11的端口。这种线程同步方法背后的思想是,对于生产者-消费者情况,如果生产者的运行速度比消费者快,那么三重缓冲可以带来一些好处,因为生产者线程不会因为等待消费者而“减速”。在我的例子中,我有一个更新速度约为120fps的物理线程和一个运行速度约为60fps的渲染线程。显然,我希望渲染线程总是尽可能地获取最新状态,但我也知道,由于速率不同,我将从物理线程中跳过很多帧。另一方面,我希望我的物理线程保持其恒定的更新速率,而

我最近使用三重缓冲区的std::atomic作为并发同步机制,创建了一个到C++11的端口。这种线程同步方法背后的思想是,对于生产者-消费者情况,如果生产者的运行速度比消费者快,那么三重缓冲可以带来一些好处,因为生产者线程不会因为等待消费者而“减速”。在我的例子中,我有一个更新速度约为120fps的物理线程和一个运行速度约为60fps的渲染线程。显然,我希望渲染线程总是尽可能地获取最新状态,但我也知道,由于速率不同,我将从物理线程中跳过很多帧。另一方面,我希望我的物理线程保持其恒定的更新速率,而不受锁定数据的较慢渲染线程的限制

最初的C代码是由remis Thinks编写的,完整的解释在他的文章中。我鼓励有兴趣阅读它的人进一步了解原始实现

可以找到我的实现

基本思想是拥有一个具有3个位置(缓冲区)的数组和一个原子标志,该标志在任何给定时间进行比较和交换,以定义哪些数组元素对应于什么状态。这样,只使用一个原子变量对数组的所有3个索引和三重缓冲背后的逻辑进行建模。缓冲区的3个位置分别命名为“脏”、“干净”和“捕捉”。生产者总是写入脏索引,并且可以翻转写入器以将脏索引与当前干净索引交换。使用者可以请求一个新的快照,该快照将当前快照索引与干净索引交换,以获取最新的缓冲区。消费者总是在捕捉位置读取缓冲区

该标志由8位无符号整数组成,其位对应于:

(未使用)(新写入)(2倍脏)(2倍干净)(2倍快照)

newWrite额外位标志由写入程序设置,并由读取器清除。读卡器可以使用此选项检查自上次快照以来是否有任何写入操作,如果没有,则不会再进行另一次快照。标记和索引可以使用简单的按位操作获得

现在确定代码:

template <typename T>
class TripleBuffer
{

public:

  TripleBuffer<T>();
  TripleBuffer<T>(const T& init);

  // non-copyable behavior
  TripleBuffer<T>(const TripleBuffer<T>&) = delete;
  TripleBuffer<T>& operator=(const TripleBuffer<T>&) = delete;

  T snap() const; // get the current snap to read
  void write(const T newT); // write a new value
  bool newSnap(); // swap to the latest value, if any
  void flipWriter(); // flip writer positions dirty / clean

  T readLast(); // wrapper to read the last available element (newSnap + snap)
  void update(T newT); // wrapper to update with a new element (write + flipWriter)

private:

  bool isNewWrite(uint_fast8_t flags); // check if the newWrite bit is 1
  uint_fast8_t swapSnapWithClean(uint_fast8_t flags); // swap Snap and Clean indexes
  uint_fast8_t newWriteSwapCleanWithDirty(uint_fast8_t flags); // set newWrite to 1 and swap Clean and Dirty indexes

  // 8 bit flags are (unused) (new write) (2x dirty) (2x clean) (2x snap)
  // newWrite   = (flags & 0x40)
  // dirtyIndex = (flags & 0x30) >> 4
  // cleanIndex = (flags & 0xC) >> 2
  // snapIndex  = (flags & 0x3)
  mutable atomic_uint_fast8_t flags;

  T buffer[3];
};
template <typename T>
TripleBuffer<T>::TripleBuffer(){

  T dummy = T();

  buffer[0] = dummy;
  buffer[1] = dummy;
  buffer[2] = dummy;

  flags.store(0x6, std::memory_order_relaxed); // initially dirty = 0, clean = 1 and snap = 2
}

template <typename T>
TripleBuffer<T>::TripleBuffer(const T& init){

  buffer[0] = init;
  buffer[1] = init;
  buffer[2] = init;

  flags.store(0x6, std::memory_order_relaxed); // initially dirty = 0, clean = 1 and snap = 2
}

template <typename T>
T TripleBuffer<T>::snap() const{

  return buffer[flags.load(std::memory_order_consume) & 0x3]; // read snap index
}

template <typename T>
void TripleBuffer<T>::write(const T newT){

  buffer[(flags.load(std::memory_order_consume) & 0x30) >> 4] = newT; // write into dirty index
}

template <typename T>
bool TripleBuffer<T>::newSnap(){

  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
  do {
    if( !isNewWrite(flagsNow) ) // nothing new, no need to swap
      return false;
  } while(!flags.compare_exchange_weak(flagsNow,
                                       swapSnapWithClean(flagsNow),
                                       memory_order_release,
                                       memory_order_consume));
  return true;
}

template <typename T>
void TripleBuffer<T>::flipWriter(){

  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
  while(!flags.compare_exchange_weak(flagsNow,
                                     newWriteSwapCleanWithDirty(flagsNow),
                                     memory_order_release,
                                     memory_order_consume));
}

template <typename T>
T TripleBuffer<T>::readLast(){
    newSnap(); // get most recent value
    return snap(); // return it
}

template <typename T>
void TripleBuffer<T>::update(T newT){
    write(newT); // write new value
    flipWriter(); // change dirty/clean buffer positions for the next update
}

template <typename T>
bool TripleBuffer<T>::isNewWrite(uint_fast8_t flags){
    // check if the newWrite bit is 1
    return ((flags & 0x40) != 0);
}

template <typename T>
uint_fast8_t TripleBuffer<T>::swapSnapWithClean(uint_fast8_t flags){
    // swap snap with clean
    return (flags & 0x30) | ((flags & 0x3) << 2) | ((flags & 0xC) >> 2);
}

template <typename T>
uint_fast8_t TripleBuffer<T>::newWriteSwapCleanWithDirty(uint_fast8_t flags){
    // set newWrite bit to 1 and swap clean with dirty 
    return 0x40 | ((flags & 0xC) << 2) | ((flags & 0x30) >> 2) | (flags & 0x3);
}
模板
三类缓冲
{
公众:
三重缓冲();
三重缓冲(常数T和初始值);
//不可复制行为
TripleBuffer(常量TripleBuffer&)=删除;
TripleBuffer&运算符=(常量TripleBuffer&)=删除;
T snap()const;//获取要读取的当前快照
void write(const T newT);//写入一个新值
bool newSnap();//交换为最新值(如果有)
void flipWriter();//翻转书写器位置脏/干净
T readLast();//用于读取最后一个可用元素的包装器(newSnap+snap)
void update(T newT);//使用新元素更新的包装器(write+flipWriter)
私人:
bool isNewWrite(uint_fast8_t flags);//检查newWrite位是否为1
uint_fast8_t swapSnapWithClean(uint_fast8_t标志);//交换快照和清除索引
uint_fast8_t newWriteSwapCleanWithDirty(uint_fast8_t flags);//将newWrite设置为1,并交换干净索引和脏索引
//8位标志为(未使用)(新写入)(2x脏)(2x干净)(2x快照)
//newWrite=(标志和0x40)
//dirtyIndex=(标志&0x30)>>4
//cleanIndex=(标志和0xC)>>2
//snapIndex=(标志和0x3)
可变原子单位快速8个标志;
T缓冲区[3];
};
实施:

template <typename T>
class TripleBuffer
{

public:

  TripleBuffer<T>();
  TripleBuffer<T>(const T& init);

  // non-copyable behavior
  TripleBuffer<T>(const TripleBuffer<T>&) = delete;
  TripleBuffer<T>& operator=(const TripleBuffer<T>&) = delete;

  T snap() const; // get the current snap to read
  void write(const T newT); // write a new value
  bool newSnap(); // swap to the latest value, if any
  void flipWriter(); // flip writer positions dirty / clean

  T readLast(); // wrapper to read the last available element (newSnap + snap)
  void update(T newT); // wrapper to update with a new element (write + flipWriter)

private:

  bool isNewWrite(uint_fast8_t flags); // check if the newWrite bit is 1
  uint_fast8_t swapSnapWithClean(uint_fast8_t flags); // swap Snap and Clean indexes
  uint_fast8_t newWriteSwapCleanWithDirty(uint_fast8_t flags); // set newWrite to 1 and swap Clean and Dirty indexes

  // 8 bit flags are (unused) (new write) (2x dirty) (2x clean) (2x snap)
  // newWrite   = (flags & 0x40)
  // dirtyIndex = (flags & 0x30) >> 4
  // cleanIndex = (flags & 0xC) >> 2
  // snapIndex  = (flags & 0x3)
  mutable atomic_uint_fast8_t flags;

  T buffer[3];
};
template <typename T>
TripleBuffer<T>::TripleBuffer(){

  T dummy = T();

  buffer[0] = dummy;
  buffer[1] = dummy;
  buffer[2] = dummy;

  flags.store(0x6, std::memory_order_relaxed); // initially dirty = 0, clean = 1 and snap = 2
}

template <typename T>
TripleBuffer<T>::TripleBuffer(const T& init){

  buffer[0] = init;
  buffer[1] = init;
  buffer[2] = init;

  flags.store(0x6, std::memory_order_relaxed); // initially dirty = 0, clean = 1 and snap = 2
}

template <typename T>
T TripleBuffer<T>::snap() const{

  return buffer[flags.load(std::memory_order_consume) & 0x3]; // read snap index
}

template <typename T>
void TripleBuffer<T>::write(const T newT){

  buffer[(flags.load(std::memory_order_consume) & 0x30) >> 4] = newT; // write into dirty index
}

template <typename T>
bool TripleBuffer<T>::newSnap(){

  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
  do {
    if( !isNewWrite(flagsNow) ) // nothing new, no need to swap
      return false;
  } while(!flags.compare_exchange_weak(flagsNow,
                                       swapSnapWithClean(flagsNow),
                                       memory_order_release,
                                       memory_order_consume));
  return true;
}

template <typename T>
void TripleBuffer<T>::flipWriter(){

  uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
  while(!flags.compare_exchange_weak(flagsNow,
                                     newWriteSwapCleanWithDirty(flagsNow),
                                     memory_order_release,
                                     memory_order_consume));
}

template <typename T>
T TripleBuffer<T>::readLast(){
    newSnap(); // get most recent value
    return snap(); // return it
}

template <typename T>
void TripleBuffer<T>::update(T newT){
    write(newT); // write new value
    flipWriter(); // change dirty/clean buffer positions for the next update
}

template <typename T>
bool TripleBuffer<T>::isNewWrite(uint_fast8_t flags){
    // check if the newWrite bit is 1
    return ((flags & 0x40) != 0);
}

template <typename T>
uint_fast8_t TripleBuffer<T>::swapSnapWithClean(uint_fast8_t flags){
    // swap snap with clean
    return (flags & 0x30) | ((flags & 0x3) << 2) | ((flags & 0xC) >> 2);
}

template <typename T>
uint_fast8_t TripleBuffer<T>::newWriteSwapCleanWithDirty(uint_fast8_t flags){
    // set newWrite bit to 1 and swap clean with dirty 
    return 0x40 | ((flags & 0xC) << 2) | ((flags & 0x30) >> 2) | (flags & 0x3);
}
模板
TripleBuffer::TripleBuffer(){
T dummy=T();
缓冲区[0]=虚拟;
缓冲区[1]=虚拟;
缓冲区[2]=虚拟;
flags.store(0x6,std::memory_order_released);//最初脏=0,干净=1,快照=2
}
模板
TripleBuffer::TripleBuffer(常量T和初始化){
缓冲区[0]=init;
缓冲区[1]=init;
缓冲区[2]=init;
flags.store(0x6,std::memory_order_released);//最初脏=0,干净=1,快照=2
}
模板
T TripleBuffer::snap()常量{
返回缓冲区[flags.load(std::memory_order_consume)&0x3];//读取快照索引
}
模板
void TripleBuffer::write(常数T newT){
缓冲区[(flags.load(std::memory\u order\u consume)&0x30)>>4]=newT;//写入脏索引
}
模板
bool-TripleBuffer::newSnap(){
uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
做{
如果(!isNewWrite(flagsNow))//没有新内容,则无需交换
返回false;
}而(!flags.compare_exchange_弱(flagsNow,
swapSnapWithClean(flagsNow),
内存\u顺序\u释放,
内存(顺序消耗);
返回true;
}
模板
void TripleBuffer::flipWriter(){
uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
而(!flags.compare_exchange_弱(flagsNow,
新写入的WAPCleanWithDirty(flagsNow),
内存\u顺序\u释放,
内存(顺序消耗);
}
模板
T TripleBuffer::readLast(){
newSnap();//获取最近的值
return snap();//返回它
}
模板
void TripleBuffer::update(T newT){
写入(newT);//写入新值
flipWriter();//为下一次更新更改脏/干净的缓冲区位置
}
模板
bool TripleBuffer::isNewWrite(uint\u fast8\u t标志){
//检查新写入位是否为1
返回((标志&0x40)!=0);
}
模板
uint_fast8_t TripleBuffer::swapSnapWithClean(uint_fast8_t标志){
//将卡扣换成干净的卡扣
返回(标志&0x30)|((标志&0x3)>2);
}
模板
uint_fast8_t TripleBuffer::newWriteSwapCleanWithDirty(uint_fast8_t标志){
//将newWrite位设置为1,并用dirty交换clean
返回0x40 |((标志和0xC)>2)|(标志和0x3);
}
如您所见,我已决定使用释放-消耗模式进行内存排序。 存储的释放(内存\u顺序\u释放)确保当前内存中没有写操作
uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
do
{
    if( !isNewWrite(flagsNow)) return false; // nothing new, no need to swap
} while(!flags.compare_exchange_weak(flagsNow, swapSnapWithClean(flagsNow), memory_order_release, memory_order_consume));
uint_fast8_t flagsNow(flags.load(std::memory_order_consume));
while(!flags.compare_exchange_weak(flagsNow, newWriteSwapCleanWithDirty(flagsNow), memory_order_release, memory_order_consume));