Для традиционного одноблочного кругового буфера я думаю, что это просто невозможно сделать безопасно с атомарными операциями. Вы должны сделать так много за одно чтение. Предположим, у вас есть структура, которая имеет это:
uint8_t* buf;
unsigned int size; // Actual max. buffer size
unsigned int length; // Actual stored data length (suppose in write prohibited from being > size)
unsigned int offset; // Start of current stored data
При чтении необходимо выполнить следующее (в любом случае, как я это реализовал, вы можете поменять местами некоторые шаги, как я расскажу позже):
- Проверьте, не превышает ли считанная длина сохраненную длину
- Проверьте, не превышают ли смещение + длина чтения границы буфера
- Считать данные
- Увеличение смещения, уменьшение длины
Что вам, безусловно, нужно сделать синхронизированным (таким атомарным), чтобы эта работа работала? На самом деле объедините шаги 1 и 4 в один атомарный шаг, или чтобы уточнить: сделать это синхронизировано:
- проверьте read_length, это может быть что-то вроде
read_length=min(read_length,length);
- уменьшение длины с read_length:
length-=read_length
- получить локальную копию со смещения
unsigned int local_offset = offset
- увеличить смещение с длиной чтения:
offset+=read_length
После этого вы можете просто сделать memcpy (или что-то еще), начиная с вашего local_offset, проверить, превышает ли ваше чтение размер буфера по кругу (разделенный на 2 memcpy), .... Это «вполне» потокобезопасный, ваш метод записи все еще может записывать поверх памяти, которую вы читаете, поэтому убедитесь, что ваш буфер действительно достаточно большой, чтобы минимизировать эту возможность.
Теперь, хотя я могу представить, что вы можете комбинировать 3 и 4 (я думаю, это то, что они делают в случае со связанным списком) или даже 1 и 2 в атомарных операциях, я не вижу, чтобы вы делали всю эту сделку в одной атомарной операции :).
Однако вы можете отказаться от проверки длины, если ваши потребители очень умны и всегда будут знать, что читать. Тогда вам также понадобится новая переменная woffset, потому что старый метод (offset + length)% size для определения смещения записи больше не будет работать. Обратите внимание, что это близко к случаю связанного списка, где вы фактически всегда читаете один элемент (= фиксированный, известный размер) из списка. Также здесь, если вы сделаете это круговым связанным списком, вы можете читать много или писать в позицию, которую вы читаете в данный момент!
Наконец: мой совет, просто используйте блокировку, я использую класс CircularBuffer, абсолютно безопасный для чтения и записи) для видеопотока в режиме реального времени 720p60, и у меня вообще не возникает проблем со скоростью из-за блокировки.