マルチスレッドコンピューティング環境におけるハザードポインタは、ロックフリーデータ構造のノードの動的メモリ管理によって生じる問題を解決するアプローチの 1 つです。これらの問題は、一般的に自動ガベージコレクションがない環境でのみ発生します。[ 1 ]ポインタ型ではありますが、スマートポインタではありません。
比較交換プリミティブを使用するロックフリーデータ構造はすべて、ABA 問題に対処する必要があります。たとえば、侵入型リンクリストとして表現されるロックフリースタックでは、1 つのスレッドがスタックの先頭から項目をポップしようとします ( A→ B→ C)。スレッドは上から 2 番目の値を記憶しB、 を実行します。残念ながら、この操作の途中で、別のスレッドが 2 回のポップを実行してからスタックの先頭にプッシュバックしたため、スタックは ( → ) の状態になります。比較交換はと の交換に成功し、結果としてスタックにはガベージ (解放された要素へのポインタ) が含まれます。head.compareAndSwap(B,A)AACheadBB
さらに、次の形式のコードを含むロックフリーアルゴリズム
Node current = x.head (); // " x.head ()" からのロードはアトミックであると仮定しますNode next = current.next ( ) ; // このロードもアトミックであると仮定します自動ガベージコレクションがない場合、別の大きな問題が発生します。この 2 行の間で、別のスレッドが によって指されるノードをポップしてx.head()解放する可能性があり、その結果、2 行目の を介したメモリ アクセスはcurrent解放されたメモリを読み取ることになります (実際には、そのメモリは別のスレッドによってまったく異なる目的ですでに使用されている可能性があります)。
ハザードポインタを使用すると、これらの両方の問題を解決できます。ハザードポインタシステムでは、各スレッドが、現在アクセスしているノードを示すハザードポインタのリストを保持します。(多くのシステムでは、この「リスト」は1つ[ 1 ] [ 2 ]または2つの要素に限定される可能性があります。)ハザードポインタリスト上のノードは、他のスレッドによって変更または解放されてはなりません。
各リーダー スレッドは、「ハザード ポインタ」と呼ばれる、単一書き込み/複数リーダーの共有ポインタを所有しています。リーダー スレッドがマップのアドレスをハザード ポインタに割り当てると、基本的に他のスレッド(書き込みスレッド)に対して「私はこのマップを読み込んでいます。必要であれば置き換えても構いませんが、内容を変更したり、絶対に触ったりしないでください
delete」と通知していることになります。
— Andrei Alexandrescuおよび Maged Michael、「ハザードポインタを使用したロックフリーデータ構造」[ 2 ]
スレッドがノードを削除しようとする場合、そのノードを「後で解放する」ノードのリストに追加しますが、他のスレッドのハザードリストにそのポインタが含まれなくなるまで、実際にノードのメモリを解放することはありません。この手動のガベージコレクションは、専用のガベージコレクションスレッドによって実行できます(「後で解放する」リストがすべてのスレッドで共有されている場合)。あるいは、「後で解放する」リストのクリーンアップは、各ワーカー スレッドが「pop」などの操作の一部として実行することもできます(この場合、各ワーカー スレッドは独自の「後で解放する」リストを担当できます)。
2002年、IBMのマゲド・マイケルはハザードポインタ技術に関する米国特許を申請したが[ 3 ]、2010年に申請は放棄された。
ハザードポインタの代替手段としては、参照カウント法がある。[ 1 ]
ハザードポインタは、 C++26で追加されました。これstd::hazard_pointerは、一度に最大 1 つのスレッドが所有できる、単一ライターのマルチリーダーポインタです。ヘッダー には<hazard_pointer>、C++ 標準ライブラリのスレッドサポートに関する記述があります。ハザード保護可能にするには、クラスはと呼ばれるクラスを継承するstd::hazard_ptr_obj_base<T, Del>必要があります(テンプレートパラメータDelはデフォルトで ですstd::default_delete<T>)。[ 4 ]