Поиск свободной от блокировок безопасной от RT структуры единственного устройства записи единственного читателя

Я ищу свободный от блокировок дизайн, соответствующий этому необходимому:

  • единственный писатель пишет в структуру, и единственный читатель читает из этой структуры (эта структура уже существует и безопасна для одновременного чтения-записи),
  • но в некоторое время, структура должна быть изменена устройством записи, которое затем инициализирует, переключается и пишет в новую структуру (того же типа, но с новым содержанием)
  • и в следующий раз чтения читателя, это переключается на эту новую структуру (если устройство записи умножает переключатели к новой свободной от блокировок структуре, читатель отбрасывает эти структуры, игнорируя их данные).
  • Структуры должны быть снова использованы, т.е. никакое выделение памяти "кучи" / свободный не позволяется во время операции записи/чтения/переключателя в целях RT.

Я в настоящее время реализовывал ringbuffer, содержащий несколько экземпляров этих структур; но эта реализация страдает от того, что, когда писатель использовал все структуры, существующие в ringbuffer, больше нет места для изменения от структуры... Но остальная часть ringbuffer содержит некоторые данные, которые не должны быть считаны читателем, но не могут быть снова использованы устройством записи. Как следствие ringbuffer не соответствует этой цели.

Какая-либо идея (имя или псевдореализация) свободного от блокировок дизайна? Спасибо за то, что рассмотрел эту проблему.

6
задан moala 28 February 2010 в 16:40
поделиться

2 ответа

Вы на правильном пути.

Свободная от блокировки передача фиксированных сообщений между потоками / процессами / процессорами alt text

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

переменная head записывается только производителем (как атомарное действие после записи)

tail переменная записывается только потребителем (как атомарное действие после чтения)

Ловушка: введение переменной размера или флага полного / пустого буфера; они обычно пишутся как производителем, так и потребителем и, следовательно, создают проблему.

Обычно для этой цели я использую кольцевые буферы. Самый важный урок, который я усвоил, заключается в том, что кольцевой буфер никогда не может содержать больше элементов. Таким образом, переменные заголовка и хвоста записываются производителем и потребителем.

Расширение для блоков большого / переменного размера Чтобы использовать буферы в среде реального времени, вы можете либо использовать пулы памяти (часто доступные в оптимизированной форме в операционных системах реального времени), либо отделить распределение от использования . Я считаю, что последнее соответствует вопросу.

extended queue

Если вам нужно обмениваться большими блоками, я предлагаю использовать пул с буферными блоками и передавать указатели на буферы с помощью очереди. Так что используйте третью очередь с указателями на буфер. Таким образом, распределение может быть выполнено в приложении (в фоновом режиме), и ваша часть в реальном времени имеет доступ к переменному объему памяти.

Приложение

while (blockQueue.full != true)
{
    buf = allocate block of memory from heap or buffer pool
    msg = { .... , buf };
    blockQueue.Put(msg)
}

Producer:
   pBuf = blockQueue.Get()
   pQueue.Put()

Consumer
   if (pQueue.Empty == false)
   {
      msg=pQueue.Get()
      // use info in msg, with buf pointer
      // optionally indicate that buf is no longer used
   }
0
ответ дан 18 December 2019 в 04:52
поделиться

Вот один. Ключ в том, что есть три буфера, и читатель резервирует буфер, из которого он читает. Писатель пишет в один из двух других буферов. Риск столкновения минимален. Плюс это расширяется. Просто сделайте ваши членские массивы на один элемент длиннее, чем количество читателей плюс количество писателей.

class RingBuffer
{
  RingBuffer():lastFullWrite(0)
  { 
    //Initialize the elements of dataBeingRead to false
    for(unsigned int i=0; i<DATA_COUNT; i++)
    {
      dataBeingRead[i] = false;
    } 
  }

  Data read()
  {
    // You may want to check to make sure write has been called once here
    // to prevent read from grabbing junk data. Else, initialize the elements
    // of dataArray to something valid.
    unsigned int indexToRead = lastFullWriteIndex;
    Data dataCopy;
    dataBeingRead[indexToRead] = true;
    dataCopy = dataArray[indexToRead];
    dataBeingRead[indexToRead] = false;
    return dataCopy;
  }

  void write( const Data& dataArg )
  {
    unsigned int writeIndex(0);

    //Search for an unused piece of data.
    // It's O(n), but plenty fast enough for small arrays.
    while( true == dataBeingRead[writeIndex] && writeIndex < DATA_COUNT )
    {
      writeIndex++;
    }  

    dataArray[writeIndex] = dataArg;

    lastFullWrite = &dataArray[writeIndex];
  }

private:
  static const unsigned int DATA_COUNT;
  unsigned int lastFullWrite;
  Data dataArray[DATA_COUNT];
  bool dataBeingRead[DATA_COUNT];
};

Примечание: здесь написано, что есть две копии для чтения ваших данных. Если вы передаете данные из функции чтения с помощью ссылочного аргумента, вы можете сократить их до одной копии.

0
ответ дан 18 December 2019 в 04:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: