我正在尝试找到一种方法来制作无锁或非阻塞方式来为单个消费者/单个消费者创建一个环形缓冲区,这将覆盖缓冲区中最旧的数据。如果缓冲区已满,我已经阅读了很多无锁算法,当您“返回 false”时它们会起作用——即,不要添加;但是当您需要覆盖最旧的数据时,我什至找不到讨论如何执行此操作的伪代码。
我正在使用 GCC 4.1.2(工作限制,我无法升级版本......)并且我有 Boost 库,过去我制作了自己的 Atomic< T > 变量类型,它非常接近于upcomming 规范(它并不完美,但它是线程安全的并且可以满足我的需求)。
当我想到它时,我认为使用这些原子应该真正解决这个问题。关于我在想什么的一些粗略的伪代码:
template< typename T , unsigned int Size>
class RingBuffer {
private:
Atomic<unsigned int> readIndex;
Atomic<unsigned int> writeIndex;
enum Capacity { size = Size };
T* buf;
unsigned int getNextIndex(unsigned int i)
{
return (i + 1 ) % size;
}
public:
RingBuffer() { //create array of size, set readIndex = writeIndex = 0 }
~RingBuffer() { //delete data }
void produce(const T& t)
{
if(writeIndex == getNextIndex(readIndex)) //1
{
readIndex = getNextIndex(readIndex); //2
}
buf[writeIndex] = t;
writeIndex = getNextIndex(writeIndex); //3
}
bool consume(T& t)
{
if(readIndex == writeIndex) //4
return false;
t = buf[readIndex];
readIndex = getNexIndex(readIndex); //5
return true;
}
};
据我所知,这里没有死锁情况,所以我们可以避免这种情况(如果我上面的实现即使在伪代码级别上也是错误的,那么建设性的批评总是值得赞赏的)。但是,我能找到的 BIG 比赛条件是:
让我们假设缓冲区已满。即writeIndex +1 = readIndex;(1) 发生,就像调用 consumer 一样。and is true (4) is false, 所以我们移动到从缓冲区读取 (5) 发生了,并且 readIndex 提前了一个(所以实际上缓冲区中的空间 (2) 发生了,提前 readIndex AGAIN,因此失去价值。
基本上,这是作家必须修改读者的经典问题,导致竞争条件。如果我每次访问它时都没有真正阻止整个列表,我想不出一种方法来防止这种情况发生。我错过了什么??