在C中寻找正确的环形缓冲区实现

时间:2012-09-04 13:05:41

标签: c multithreading producer-consumer lock-free circular-buffer

我在C中寻找具有以下特征的环形缓冲区实现(或伪代码):

  • 多个生产者单一消费者模式(MPSC)
  • 消费者阻止空
  • 制作人已完全阻止
  • 无锁(我期待很高的争用)

到目前为止,我一直只使用SPSC缓冲区 - 每个生产者一个 - 但我想避免消费者不断旋转以检查所有输入缓冲区的新数据(也许是为了摆脱一些封送我系统中的线程。)

我在Intel机器上开发Linux。

2 个答案:

答案 0 :(得分:4)

请参阅liblfds,一个无锁的MPMC环形缓冲区。它不会阻止所有无锁数据结构不倾向于这样做,因为无锁是指避免阻塞; 需要处理这个问题,当数据结构通过NULL返回给您时 - 如果您尝试读取空值,则返回NULL,但不符合您的要求写作时;在这里,它会抛弃最古老的元素并为你写下来。

但是,只需要进行一些小修改即可获得该行为。

但可能有更好的解决方案。环形缓冲区的棘手部分是在完全获取最旧的前一个元素并重新使用它之后。你不需要这个。我认为你可以采用SPSC内存屏障仅循环缓冲区并使用原子操作重写它。这将是{em>很多更高效的liblfds中的MPMC振铃器(它是队列和堆栈的组合)。

答案 1 :(得分:3)

我想我有你想要的东西。它是一个无锁的环缓冲区实现,可以阻止生产者/消费者。您只需要访问原子基元 - 在本例中我将使用gcc的sync函数。

它有一个已知的错误 - 如果你将缓冲区溢出超过100%,则无法保证队列仍然是FIFO(它最终仍会处理它们)。

此实现依赖于读取/写入缓冲区元素作为原子操作(指针几乎可以保证)

struct ringBuffer
{
   void** buffer;
   uint64_t writePosition;
   size_t size;
   sem_t* semaphore;
}

//create the ring buffer
struct ringBuffer* buf = calloc(1, sizeof(struct ringBuffer));
buf->buffer = calloc(bufferSize, sizeof(void*));
buf->size = bufferSize;
buf->semaphore = malloc(sizeof(sem_t));
sem_init(buf->semaphore, 0, 0);

//producer
void addToBuffer(void* newValue, struct ringBuffer* buf)
{
   uint64_t writepos = __sync_fetch_and_add(&buf->writePosition, 1) % buf->size;

   //spin lock until buffer space available
   while(!__sync_bool_compare_and_swap(&(buf->buffer[writePosition]), NULL, newValue));
   sem_post(buf->semaphore);
}    

//consumer
void processBuffer(struct ringBuffer* buf)
{
   uint64_t readPos = 0;
   while(1)
   {
       sem_wait(buf->semaphore);

       //process buf->buffer[readPos % buf->size]
       buf->buffer[readPos % buf->size] = NULL;
       readPos++;
   }
}