22

Herb Sutter のアトミックに関する講演から抜粋した次のコード スニペットを考えてみましょう。

smart_ptr クラスには、参照カウントrefsを含む control_block_ptr という pimpl オブジェクトが含まれています。

// Thread A:
// smart_ptr copy ctor
smart_ptr(const smart_ptr& other) {
  ...
  control_block_ptr = other->control_block_ptr;
  control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
  ...
}

// Thread D:
// smart_ptr destructor
~smart_ptr() {
  if (control_block_ptr->refs.fetch_sub(1, memory_order_acq_rel) == 1) {
    delete control_block_ptr;
  }
}

Herb Sutter は、「アクションに基づいて誰も何もしない」ため、スレッド A の参照のインクリメントは memory_order_relaxed を使用できると述べています。ここで、memory_order_relaxed を理解しているので、ある時点でrefsが N に等しく、2 つのスレッド A と B が次のコードを実行する場合:

control_block_ptr->refs.fetch_add(1, memory_order_relaxed);

その場合、両方のスレッドがrefsの値が N であることを認識し、両方とも N+1 をそれに書き戻すことがあります。これは明らかに機能せず、デストラクタと同じように memory_order_acq_rel を使用する必要があります。どこが間違っていますか?

EDIT1: 次のコードを検討してください。

atomic_int refs = N; // at time t0. 

// [Thread 1]
refs.fetch_add(1, memory_order_relaxed); // at time t1. 

// [Thread 2]
n = refs.load(memory_order_relaxed);   // starting at time t2 > t1
refs.fetch_add(1, memory_order_relaxed);
n = refs.load(memory_order_relaxed);

fetch_addの呼び出し前にスレッド 2 によって観察された参照の値は何ですか? N または N+1 のどちらかでしょうか? fetch_add の呼び出し後にスレッド 2 によって観察される参照の値は何ですか? 少なくとも N+2 である必要がありますか?

【トークURL:C++ & Beyond 2012 - http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2 (@1: 20:00)]

4

3 に答える 3

10

エミュレートするBoost.Atomicライブラリは、同様の参照カウントの例と説明std::atomicを提供しており、理解に役立つ場合があります。

memory_order_relaxedオブジェクトへの新しい参照は、既存の参照からのみ形成でき、あるスレッドから別のスレッドに既存の参照を渡すには、必要な同期がすでに提供されている必要があります。

別のスレッドでオブジェクトを削除する前に、(既存の参照を介して) 1 つのスレッドでオブジェクトへの可能なアクセスを強制することが重要です。これは、参照をドロップした後の「解放」操作 (この参照を介したオブジェクトへのアクセスは明らかに以前に行われている必要があります) と、オブジェクトを削除する前の「取得」操作によって実現されます。

memory_order_acq_relfetch_sub 操作に使用することは可能ですが、これにより、参照カウンターがまだゼロに達していない場合に不要な「取得」操作が発生し、パフォーマンスが低下する可能性があります。

于 2014-12-31T05:05:49.683 に答える
2

これはかなり紛らわしいので (少なくとも私にとっては)、1 つの点について部分的に説明します。

(...)次に、両方のスレッドが refs の値が N であると認識し、両方が N+1 をそれに書き戻すことがあります(...)

この回答の@AnthonyWilliams によると、上記の文は次のように間違っているようです。

「最新」の値であることを保証する唯一の方法は、exchange()、compare_exchange_strong()、または fetch_add() などの読み取り-変更-書き込み操作を使用することです。読み取り-変更-書き込み操作には、常に「最新」の値を操作するという追加の制約があるため、一連のスレッドによる一連の ai.fetch_add(1) 操作は、重複やギャップのない一連の値を返します。追加の制約がない場合でも、どのスレッドがどの値を参照するかはまだ保証されていません。

したがって、権限の引数を考えると、両方のスレッドが値がNからN+1になるのを見ることは不可能だと思います。

于 2016-12-08T17:25:55.217 に答える