单一生产者,单一消费者环形缓冲区的最小限制内存排序?

时间:2016-01-23 18:46:57

标签: c++ multithreading c++11 memory-barriers

我有一个RingBuffer,为一个消费者和一个生产者提供服务,并使用两个整数来检测新数据:

_lastReadIndex
_lastWrittenIndex

所以当这两个值不相等时,ringbuffer中有未读数据。

当项目添加到ringbuffer时,Producer 递增(以及带有环形缓冲区大小的模块到环绕)_lastWrittenIndex

消费者旋转,读取两个值,检查新数据以及何时存在,增量(和模数)_lastReadIndex

三个突出的术语强调了多线程和内存障碍的要求。

考虑到英特尔的内存模型,我能在多长时间内放宽这种设计的内存排序?我相信英特尔的内存模型允许将早期商店的负载重新订购到不同的地址吗?

使用C ++ 11原子库std::memory_order_xxxx等进行编辑

1 个答案:

答案 0 :(得分:1)

在其他任何事情之前你必须做的一些事情:

模拟读取和写入点,但保持_lastReadIndex_lastWrittenIndex完好无损,以了解您可用的数据量,丢失的数量,或者如果在完整后超过读者,可能会阻止写入者周期。

而且,非常重要的是,尽量避免共享 - 将读写器变量放在不同的缓存行上。

现在,问你的问题:

如果您尝试移植,则代码中要求的内存排序不应考虑架构。标准的原子功能可以解决这个问题。 在递增写入索引之前,您只需要确保缓冲区中的数据可用,这意味着在增量上释放语义。 您还必须确保编写器将数据写入内存,而不是优化以仅保留在寄存器中。

newIndex = _lastWrittenIndex+1;
buffer[newIndex % bufSize] = newData;
atomic_store( &_lastWrittenIndex, newIndex, memory_order_release );

在x86 / 64上,这将与:

相同
newIndex = _lastWrittenIndex+1;
buffer[newIndex % bufSize] = newData;
// release semantics means reorder barrier before action:
barrier(); // translates to `asm volatile("":::"memory");`
*(volatile int*)_lastWrittenIndex = newIndex;

当编写访问_lastWrittenIndex的代码不超过绝对必要时,如上所述,您也可以声明它不稳定,但请记住仍然需要屏障!