バッファ内の最も古いデータを上書きするシングルコンシューマ/シングルコンシューマ用のリングバッファを作成するロックフリーまたは非ブロッキングの方法を作成する方法を見つけようとしています。バッファがいっぱいの場合に「falseを返す」ときに機能するロックフリーアルゴリズムをたくさん読みました。つまり、追加しないでください。しかし、最も古いデータを上書きする必要があるときにそれを行う方法について説明している疑似コードさえ見つかりません。
私は GCC 4.1.2 を使用しており (制限があり、バージョンをアップグレードできません...)、Boost ライブラリがあり、過去に独自の Atomic< T > 変数型を作成しました。今後の仕様(完全ではありませんが、スレッドセーフであり、必要なことを行います)。
考えてみると、これらのアトミックを使用すれば問題は解決するはずだと思いました。私が考えていたことに関する大まかな疑似コード:
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) は、consume が呼び出されたときに発生します。そして true (4) は false であるため、バッファから読み取るように移動します (5) が発生し、readIndex が 1 つ進められます (したがって、実際には、バッファ内にスペースがあり (2) が発生し、readIndex が AGAIN を進めます。したがって、価値を失う。
基本的に、ライターがリーダーを変更しなければならず、競合状態が発生するという古典的な問題です。アクセスするたびにリスト全体を実際にブロックしない限り、これを防ぐ方法は考えられません。私は何が欠けていますか??