2

Windows 用の C++ でプリミティブ データ型の学習演習として、非常に軽量なアトミック ラッパーを実装しています。代入演算子の実装についていくつか簡単な質問があります。以下の 2 つの実装を検討してください。

// Simple assignment
Atomic& Atomic::operator=(const Atomic& other)
{
    mValue = other.mValue;
    return *this;
}

// Interlocked assignment
Atomic& Atomic::operator=(const Atomic& other)
{
    _InterlockedExchange(&mValue, other.mValue);
    return *this;
}

mValueそれが正しい型であり、Atomicクラスがそれをメンバーとして持っていると仮定します。

  1. スレッドセーフな代入演算子が_InterlockedExchange必要ですか、それとも単純な実装でスレッドセーフを保証するのに十分ですか?
  2. 単純な代入がスレッドセーフである場合、このクラスの代入演算子を実装する必要さえありますか? コンパイラのデフォルトで十分ですよね?
  3. 単純な割り当てが Windows でスレッドセーフである場合、他のプラットフォームでもスレッドセーフですか? _InterlockedExchange他のプラットフォームでスレッドの安全性を保証するために必要なものはありますか?
4

3 に答える 3

4

C++ アトミック型が対処する3 つの問題があります。まず、読み取りまたは書き込みの途中でスレッドの切り替えが発生し、データが文字化けする可能性があります。これは「引き裂き」として知られています。第 2 に、各プロセッサには独自のデータ キャッシュがあり、あるスレッドに値を書き込んでも、別のプロセッサのキャッシュの値が更新されるとは限りません。これは「古いデータ」です。第 3 に、結果がさまざまな規則に違反しない場合、コンパイラは命令を並べ替えて効率を向上させることができます。スレッド間でデータが共有されることを伝えないと、驚くようなことが起こるかもしれません。

std::atomic(または原子性を確保するためのさまざまな実装固有のメカニズム) を使用すると、3 つの問題すべてに対処できます。それらをバイパスする正当な理由はありません。ライブラリとコンパイラの作成者は、正しく機能する効率的なコードを生成する方法を、あなたよりもよく知っていることはほぼ確実です。

于 2013-07-08T13:05:49.863 に答える
2
  1. それは のサイズと配置に依存し、オブジェクトの配置mValue方法にさらに依存します。Atomic一般に、サイズが CPU レジスタのサイズと等しく、適切に配置されている場合、書き込みはアトミックです。(例: 32 ビット CPU 上の 32 ビット境界に整列された 32 ビット データ型は、原子的に書き込まれます。このシナリオでは、64 ビット データ型の原子性は保証されません。)
  2. mValue正しい、コンパイラのデフォルトの実装は、それが唯一のインスタンスデータメンバーであると仮定して、あなたが与えた「単純な割り当て」の例と同じになります。
  3. 通常、そうです。これは、特定のオペレーティング システムというよりも、CPU とアーキテクチャの機能によるものです。書き込みmValueがアトミックでない場合は、同様の構成が必要になります。(たとえば、組み込みのアトミック操作に関する GCC ドキュメントを参照してください。)
于 2013-07-08T12:24:21.210 に答える