C++ C++;11原子内存排序-这是松弛(释放-消耗)排序的正确用法吗?
我最近使用三重缓冲区的std::atomic作为并发同步机制,创建了一个到C++11的端口。这种线程同步方法背后的思想是,对于生产者-消费者情况,如果生产者的运行速度比消费者快,那么三重缓冲可以带来一些好处,因为生产者线程不会因为等待消费者而“减速”。在我的例子中,我有一个更新速度约为120fps的物理线程和一个运行速度约为60fps的渲染线程。显然,我希望渲染线程总是尽可能地获取最新状态,但我也知道,由于速率不同,我将从物理线程中跳过很多帧。另一方面,我希望我的物理线程保持其恒定的更新速率,而不受锁定数据的较慢渲染线程的限制 最初的C代码是由remis Thinks编写的,完整的解释在他的文章中。我鼓励有兴趣阅读它的人进一步了解原始实现 可以找到我的实现 基本思想是拥有一个具有3个位置(缓冲区)的数组和一个原子标志,该标志在任何给定时间进行比较和交换,以定义哪些数组元素对应于什么状态。这样,只使用一个原子变量对数组的所有3个索引和三重缓冲背后的逻辑进行建模。缓冲区的3个位置分别命名为“脏”、“干净”和“捕捉”。生产者总是写入脏索引,并且可以翻转写入器以将脏索引与当前干净索引交换。使用者可以请求一个新的快照,该快照将当前快照索引与干净索引交换,以获取最新的缓冲区。消费者总是在捕捉位置读取缓冲区 该标志由8位无符号整数组成,其位对应于: (未使用)(新写入)(2倍脏)(2倍干净)(2倍快照) newWrite额外位标志由写入程序设置,并由读取器清除。读卡器可以使用此选项检查自上次快照以来是否有任何写入操作,如果没有,则不会再进行另一次快照。标记和索引可以使用简单的按位操作获得 现在确定代码: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的渲染线程。显然,我希望渲染线程总是尽可能地获取最新状态,但我也知道,由于速率不同,我将从物理线程中跳过很多帧。另一方面,我希望我的物理线程保持其恒定的更新速率,而
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));