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