Go の sync.Once におけるアトミック操作の適切な使用法
Go の sync.Once 実装のコンテキストでは、 Done フラグを設定するときの通常の代入と atomic.StoreUint32 操作の区別。
間違った実装
当初、once.go の Do 関数は次のアプローチを利用していました。 :
if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
この実装では、Do が返されたときに f の実行が完了することを保証できません。 Do への 2 つの同時呼び出しにより、最初の呼び出しは正常に f を呼び出すことになりますが、2 番目の呼び出しは、f が完了していないにもかかわらず、完了したものとみなして途中で戻ります。
アトミック ストア オペレーション
この問題に対処するために、Go は atomic.StoreUint32 オペレーションを採用しています。通常の割り当てとは異なり、atomic.StoreUint32 は、更新された Done フラグを他のゴルーチンに確実に可視化します。
メモリ モデルの考慮事項
sync.Once でのアトミック操作の使用は、基礎となるマシンのメモリ モデルには主に影響されません。 Go のメモリ モデルは統一的な抽象化として機能し、特定のメモリ モデルに関係なく、さまざまなハードウェア プラットフォーム間で一貫した動作を保証します。
最適化された高速パス
パフォーマンスを最適化するには、同期します。 .Once は、done フラグがすでに設定されている一般的なシナリオに高速パスを採用します。この高速パスは、atomic.LoadUint32 を利用して、ミューテックスを取得せずに完了フラグをチェックします。フラグが設定されている場合、関数はすぐに戻ります。
ミューテックスとアトミック ストアを使用した低速パス
高速パスが失敗した場合 (つまり、done は最初は設定されていません)、ゆっくりとした道が入ります。ミューテックスは、1 つの呼び出し元だけが f の実行に進むことができるようにするために取得されます。 f が完了すると、atomic.StoreUint32 を使用して Done フラグが設定され、他のゴルーチンから見えるようになります。
同時読み取り
Done フラグが設定されていても原子的には、同時読み取りが安全になるわけではありません。保護されたクリティカル セクションの外側でフラグを読み取るには、atomic.LoadUint32 を使用する必要があります。ただし、相互排他を提供するミューテックスにより、クリティカル セクション内の直接読み取りは安全です。
要約すると、Go の sync.Once は atomic.StoreUint32 を利用して、done フラグの一貫した目に見える変更を保証します。基礎となるメモリ モデルを変更し、データ競合を回避します。アトミック操作とミューテックスの組み合わせにより、パフォーマンスの最適化と正確性の保証の両方が提供されます。
以上がGo の `sync.Once` は、通常の代入ではなく `atomic.StoreUint32` を使用して `done` フラグを設定するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。