【发布时间】:2015-09-11 14:45:48
【问题描述】:
我实现了一个单生产者单消费者无锁循环缓冲区。参考实现位于:http://home.comcast.net/~lang.dennis/code/ring/ring.html(基线实现,从顶部开始的第一个列表)。
编辑:供参考原代码:
template <class T, size_t RingSize>
class RingBuffer
{
public:
RingBuffer(size_t size = 100)
: m_size(size), m_buffer(new T[size]), m_rIndex(0), m_wIndex(0)
{ assert(size > 1 && m_buffer != NULL); }
~RingBuffer()
{ delete [] m_buffer; };
size_t Next(size_t n) const
{ return (n+1)%m_size; }
bool Empty() const
{ return (m_rIndex == m_wIndex); }
bool Full() const
{ return (Next(m_wIndex) == m_rIndex); }
bool Put(const T& value)
{
if (Full())
return false;
m_buffer[m_wIndex] = value;
m_wIndex = Next(m_wIndex);
return true;
}
bool Get(T& value)
{
if (Empty())
return false;
value = m_buffer[m_rIndex];
m_rIndex = Next(m_rIndex);
return true;
}
private:
T* m_buffer;
size_t m_size;
// volatile is only used to keep compiler from placing values in registers.
// volatile does NOT make the index thread safe.
volatile size_t m_rIndex;
volatile size_t m_wIndex;
};
Mod:我将读写索引存储在局部变量中,并且只在表达式中使用局部变量。我在从函数(get 和 put)返回之前更新它们。
为清楚起见,get 函数:
bool Get(T& value)
{
size_t w=m_wIndex;
size_t r=m_rIndex;
if (Empty(w,r))
return false;
value = m_buffer[r];
//just in case the compiler decides to be extra smart
compilerbarrier();
m_rIndex = Next(r);
return true;
}
然后,我创建了单独的生产者和消费者线程:
生产者线程循环:
uint64_t i = 0;
while (i <= LOOPS) {
if (buf.put(i)) {
i += 1;
}
}
consumerthread.join(); //pseudocode: wait for the consumer to finish
消费者线程循环:
uint64_t i=0;
while (i < LOOPS) {
buf.get(i);
}
生产者将整数 [0, LOOPS] 放入缓冲区,消费者从缓冲区中一次获取一个,直到最终获得整数值 LOOPS,消费者循环终止。请注意,缓冲区的大小远小于 LOOPS。
如果我为读写索引保留 volatile 关键字,整个事情就像一个魅力。消费者循环终止,生产者返回。
但是,如果我删除 volatile 关键字,那么消费者将永远不会返回。
奇怪的是,这个消费者循环终止了:
uint64_t i=0;
while (i < LOOPS) {
buf.get(i);
fprintf(stderr,"%lu\n",i);
}
这个也结束了:
uint64_t i=0, j=0;
while (j < LOOPS) {
if(buf.get(i)) {
j=i;
}
}
发生了什么?我使用 gcc 4.8.4 编译了代码,并在运行 Ubuntu 的 64 位 intel (i3) 机器上设置了 -O3 标志。
【问题讨论】:
-
请发布SSCCE:sscce.org
-
尝试使用 -O0 标志并报告结果。我怀疑 buf.get() 正在优化
-
isvolatileusefulwiththreads.com -- 使用原子而不是易失性!
-
@Pavan Manjunath 终止。
-
在此处发布源代码。 @JonathanWakely 告诉你,
volatile不适合多线程。
标签: c++ multithreading circular-buffer