私は、ロックフリーの複数のプロデューサーと複数のコンシューマーのリングバッファーで (私の試み) 頭を悩ませてきました。アイデアの基本は、unsigned char 型と unsigned short 型の固有のオーバーフローを使用し、要素バッファーをこれらの型のいずれかに固定してから、リング バッファーの先頭に自由にループバックすることです。
問題は、私のソリューションが複数のプロデューサーに対して機能しないことです (ただし、N 個のコンシューマー、および単一のプロデューサー、単一のコンシューマーに対しては機能します)。
#include <atomic>
template<typename Element, typename Index = unsigned char> struct RingBuffer
{
std::atomic<Index> readIndex;
std::atomic<Index> writeIndex;
std::atomic<Index> scratchIndex;
Element elements[1 << (sizeof(Index) * 8)];
RingBuffer() :
readIndex(0),
writeIndex(0),
scratchIndex(0)
{
;
}
bool push(const Element & element)
{
while(true)
{
const Index currentReadIndex = readIndex.load();
Index currentWriteIndex = writeIndex.load();
const Index nextWriteIndex = currentWriteIndex + 1;
if(nextWriteIndex == currentReadIndex)
{
return false;
}
if(scratchIndex.compare_exchange_strong(
currentWriteIndex, nextWriteIndex))
{
elements[currentWriteIndex] = element;
writeIndex = nextWriteIndex;
return true;
}
}
}
bool pop(Element & element)
{
Index currentReadIndex = readIndex.load();
while(true)
{
const Index currentWriteIndex = writeIndex.load();
const Index nextReadIndex = currentReadIndex + 1;
if(currentReadIndex == currentWriteIndex)
{
return false;
}
element = elements[currentReadIndex];
if(readIndex.compare_exchange_strong(
currentReadIndex, nextReadIndex))
{
return true;
}
}
}
};
書き込みの主なアイデアは、疑似ロックとして機能する一時インデックス「scratchIndex」を使用して、writeIndex を更新し、他のプロデューサーが進行できるようにする前に、一度に 1 つのプロデューサーのみが要素バッファーにコピー構築できるようにすることでした。 . 私のアプローチが「ロックフリー」であることをほのめかして異教徒と呼ばれる前に、このアプローチが完全にロックフリーではないことを認識していますが、実際には (うまくいけば!) 通常のミューテックスを使用するよりもはるかに高速です!
私はここで(より複雑な)MPMCリングバッファソリューションを認識しています http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue、しかし私は実際に自分の考えを実験して比較していますそのアプローチに対して、それぞれが優れている場所を見つけます (または、実際に私のアプローチが完全に失敗するかどうか!)。
私が試したこと。
- compare_exchange_weak の使用
- 私が望む動作に一致するより正確な std::memory_order を使用する
- 私が持っているさまざまなインデックス間にキャッシュライン パッドを追加する
- Element 配列だけでなく要素を std::atomic にする
これは、アトミック アクセスを使用してミューテックスを使用する方法に関する私の頭の中の根本的なセグメンテーション違反に要約されると確信しています。私の頭の中でどのニューロンが大幅に失火しているかを指摘できる人には、心から感謝します! :)