C / C ++ Безблокирующий (или неблокирующий) кольцевой буфер, который ПЕРЕЗАПИСЫВАЕТ старые данные?

Я пытаюсь найти способ сделать без блокировки ИЛИ неблокирующий способ создания кольцевого буфера для одного потребителя / одного потребителя, который перезаписывает самые старые данные int буфер. Я читал много алгоритмов блокировки, которые работают, когда вы «возвращаете false», если буфер заполнен, то есть не добавляйте; но я не могу найти даже псевдокода, который говорит о том, как это сделать, когда вам нужно перезаписать самые старые данные.

Я использую GCC 4.1.2 (ограничение на работе, я не могу обновить версию .. . ), и у меня есть библиотеки Boost, и в прошлом я создал свой собственный тип переменной Atomic , который довольно точно соответствует будущей спецификации (он не идеален, но он потокобезопасен и делает то, что мне нужно).

Когда я подумал об этом, я подумал, что использование этих атомиков действительно должно решить проблему. какой-то грубый псевдокод относительно того, о чем я думал:

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;
}

};

Насколько я могу судить, здесь нет тупиковых ситуаций, так что мы в безопасности (если моя реализация выше неверна даже на уровне псевдокода , конструктивная критика всегда приветствуется). Я подумал, что использование этих атомиков действительно должно решить проблему. какой-то грубый псевдокод относительно того, о чем я думал:

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;
}

};

Насколько я могу судить, здесь нет тупиковых ситуаций, поэтому мы в безопасности от этого (если моя реализация выше неверна даже на уровне псевдокода , конструктивная критика всегда приветствуется). Я подумал, что использование этих атомиков действительно должно решить проблему. какой-то грубый псевдокод относительно того, о чем я думал:

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;
}

};

Насколько я могу судить, здесь нет тупиковых ситуаций, поэтому мы в безопасности от этого (если моя реализация выше неверна даже на уровне псевдокода , конструктивная критика всегда приветствуется). Однако я могу найти БОЛЬШОЕ состояние гонки:

предположим, что буфер заполнен. то есть writeIndex +1 = readIndex; (1) происходит так же, как вызывается потребление. и правда (4) ложно, поэтому переходим к чтению из буфера (5), а readIndex является расширенным (так что фактически в буфере есть место (2) происходит, продвигая readIndex СНОВА, таким образом, ПОТЕРЯ значение.

По сути, это классическая проблема, когда писатель должен модифицировать считыватель, вызывая состояние гонки. Фактически не блокируя весь список каждый раз, когда я обращаюсь к нему, я не могу придумать способ предотвратить это. Что мне не хватает ??

9
задан Foo Bah 10 August 2011 в 21:20
поделиться