LRUキャッシュの場合、「シンプル、高速、実用的な非ブロッキングおよびブロッキング並行キューアルゴリズム」で説明されているものと同様のロックフリーキューのアルゴリズムが必要です。
ただし、LRUキューを維持するには、キュー内のノードを削除する可能性も必要になります(末尾のノードを除く)。
私の考えは、CAS操作でノードを削除済みとしてマークし、その後、キュー内から削除されたノードを削除する単一のクリーンアップスレッドを使用することです。
ロックフリーアルゴリズムの作成は、私が最初に予想したよりも複雑であることがわかりました。だから、私の質問は:
そのようなアルゴリズムはすでに利用可能ですか?
これは私が現在使用している構造です:
全般的
- ノードの構造は次のとおりです。struct{e*entry、nextポインター、prevポインター、state uint32}
- 新しいノードごとに複数のメモリが割り当てられるのを避けるために、ノードの配列が割り当てられます。
- ノードへのポインターは、配列インデックス値と更新カウンターで構成され、単一のuint64に多重化されます。
- ノード状態は、ノードが削除されたかどうかを示す上位ビットで構成されます。残りのビットは更新カウンターとして使用されます
エンキュー
- 補助キューは未使用のノードのリストを保持し、「新しい」ノードは補助キューからデキューを介してフェッチされ、デフォルトに設定されます
- node.prevは、新しいノードがエンキューされる前に現在のテールに設定されます
デキュー
- head.next.prevは、ヘッドデキューの前にNIL値にCASされます。head.next.prevがCLEANUP(クリーンアップスレッドによって処理されている)に設定されている場合、デキューは許可されず、スレッドはCPUを解放し、最初からやり直します。
- 削除されていない状態のノードのデキューが成功すると、状態はCASで削除され、ノードは補助キューに戻されます。失敗すると(または状態がすでに削除済みに設定されている場合)、デキューされたnode.prevはNILからCLEANUPに変更され、ノードがデキューされたことをクリーンアップスレッドに通知します。その後、削除されていないノードが正常にデキューされ、CASで削除されるまで、デキューは最初からやり直します。
消去
- キュー内の削除をマークするために、状態はCASで削除され、成功するとクリーンアップキューに渡されます。失敗しても何も行われませんが、関数は戻ります。
クリーンアップスレッド
- node.prevがCLEANUPの場合、Dequeueはそれをキューから削除しました。ノードは補助キューに戻されます。
- node.prevがNILの場合、ノードはヘッドになるか、ヘッドになるか、またはデキューされようとしています。node == headの場合、クリーンアップスレッドはデキューを実行しようとします(状態が削除済みに変更されます)。失敗すると、クリーンアッププロセスが最初から始まります。
- nodeが別のノードに設定されている場合、node.prevはCLEANUPにCASされます。これにより、head.next == nodeになるとすぐに、デキューが行われるのを防ぎます。成功すると、二重リンクリストを使用してキュー内の削除が行われます。失敗すると、クリーンアッププロセスが最初から始まります。
Node.prevは、次のことを伝えるために使用されます。
- キューの前にあるノード
- ノードがヘッドになりそうです/ヘッドです
- ノードはクリーンアップスレッドによって処理されています
- ノードはデキューされます