メモリ バリアの使用の通常のパターンは、クリティカル セクションの実装に入れるものと一致しますが、プロデューサーとコンシューマーのペアに分割されます。例として、クリティカル セクションの実装は通常、次の形式になります。
while (!pShared->lock.testAndSet_Acquire()) ;
// (このループには、次のような通常のクリティカル セクションのものをすべて含める必要があります
// スピン、廃棄、
// pause() 命令、およびリソースに対する最後の手段であるギブアップ アンド ブロッキング
// ロックが利用可能になるまで。)
// 共有メモリへのアクセス。
pShared->foo = 1
v = pShared-> goo
pShared->lock.clear_Release()
上記のメモリ バリアの取得により、ロックの変更が成功する前に開始された可能性のあるすべてのロード (pShared->goo) が破棄され、必要に応じて再起動されるようになります。
リリース メモリ バリアは、共有メモリを保護するロック ワードがクリアされる前に、goo から (ローカルの) 変数 v へのロードが完了することを保証します。
典型的なプロデューサーとコンシューマーのアトミック フラグ シーンで同様のパターンがあります (それがあなたがしていることであるかどうかをサンプルで判断するのは困難ですが、アイデアを説明する必要があります)。
プロデューサーがアトミック変数を使用して、他の状態を使用する準備ができていることを示したとします。次のようなものが必要です。
pShared->goo = 14
pShared->atomic.setBit_Release()
ここでプロデューサに「書き込み」バリアがないと、goo ストアが CPU ストア キューを通過する前に、ハードウェアがアトミック ストアに到達しないという保証はありません。 (コンパイラが希望どおりに順序付けするメカニズムがある場合でも)。
消費者では
if ( pShared->atomic.compareAndSwap_Acquire(1,1) )
{
v = pShared->グー
}
ここに「読み取り」バリアがないと、アトミック アクセスが完了する前に、ハードウェアが動作せず、グーを取得していないことがわかりません。アトミック (つまり、ロック cmpxchg のような処理を行うインターロック関数で操作されるメモリ) は、それ自体に関してのみ「アトミック」であり、他のメモリではありません。
さて、言及しなければならない残りのことは、バリア構造は非常に移植性が低いということです。コンパイラは、ほとんどのアトミック操作メソッドに対して _acquire と _release のバリエーションを提供している可能性があり、これらは、それらを使用する種類の方法です。使用しているプラットフォーム (ia32 など) によっては、これらは _acquire() または _release() サフィックスなしで得られるものとまったく同じである可能性があります。これが問題となるプラットフォームは、ia64 (まだわずかにひきつっている HP を除いて事実上死んでいます) と powerpc です。ia64 には、ほとんどのロードおよびストア命令 (cmpxchg のようなアトミック命令を含む) に .acq および .rel 命令修飾子がありました。powerpc には、これに対する個別の指示があります (isync と lwsync は、それぞれ読み取りバリアと書き込みバリアを提供します)。
今。これをすべて言った。この道を行く正当な理由は本当にありますか?これらすべてを正しく行うことは、非常に困難な場合があります。コード レビューで多くの自己疑念と不安に備え、あらゆる種類のランダムなタイミング シナリオで多数の同時実行テストを行うようにしてください。避けるべき非常に正当な理由がない限り、クリティカル セクションを使用し、そのクリティカル セクションを自分で作成しないでください。