Я ищу свободный от блокировок дизайн, соответствующий этому необходимому:
Я в настоящее время реализовывал ringbuffer, содержащий несколько экземпляров этих структур; но эта реализация страдает от того, что, когда писатель использовал все структуры, существующие в ringbuffer, больше нет места для изменения от структуры... Но остальная часть ringbuffer содержит некоторые данные, которые не должны быть считаны читателем, но не могут быть снова использованы устройством записи. Как следствие ringbuffer не соответствует этой цели.
Какая-либо идея (имя или псевдореализация) свободного от блокировок дизайна? Спасибо за то, что рассмотрел эту проблему.
Вы на правильном пути.
Свободная от блокировки передача фиксированных сообщений между потоками / процессами / процессорами
Кольцевые буферы фиксированного размера могут использоваться в безблокировочной связи между потоками, процессами или процессорами, если есть один производитель и один потребитель. Некоторые проверки, которые необходимо выполнить:
переменная head записывается только производителем (как атомарное действие после записи)
tail переменная записывается только потребителем (как атомарное действие после чтения)
Ловушка: введение переменной размера или флага полного / пустого буфера; они обычно пишутся как производителем, так и потребителем и, следовательно, создают проблему.
Обычно для этой цели я использую кольцевые буферы. Самый важный урок, который я усвоил, заключается в том, что кольцевой буфер никогда не может содержать больше элементов. Таким образом, переменные заголовка и хвоста записываются производителем и потребителем.
Расширение для блоков большого / переменного размера Чтобы использовать буферы в среде реального времени, вы можете либо использовать пулы памяти (часто доступные в оптимизированной форме в операционных системах реального времени), либо отделить распределение от использования . Я считаю, что последнее соответствует вопросу.
Если вам нужно обмениваться большими блоками, я предлагаю использовать пул с буферными блоками и передавать указатели на буферы с помощью очереди. Так что используйте третью очередь с указателями на буфер. Таким образом, распределение может быть выполнено в приложении (в фоновом режиме), и ваша часть в реальном времени имеет доступ к переменному объему памяти.
Приложение
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
}
Вот один. Ключ в том, что есть три буфера, и читатель резервирует буфер, из которого он читает. Писатель пишет в один из двух других буферов. Риск столкновения минимален. Плюс это расширяется. Просто сделайте ваши членские массивы на один элемент длиннее, чем количество читателей плюс количество писателей.
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];
};
Примечание: здесь написано, что есть две копии для чтения ваших данных. Если вы передаете данные из функции чтения с помощью ссылочного аргумента, вы можете сократить их до одной копии.