2016-09-09 12 views
3

これは確認の質問です。私が詳細を正しく取得していることを確認するために、—言語弁護士を歓迎します。`std :: shared_ptr`のコピーコンストラクタは` reset() `に対してアトミックですか?

std::shared_ptrを次のコードに使用できることを知りたいので、atomic_shared_ptrと書き直す必要はありません。この例は単純化されているが、shared_ptrの(* 1)とreset()の呼び出し(* 2)の間の単一のインスタンスexample内の可能な競合状態である。

pのプレーンポインタはここでは機能しません。テストとsome_predicateへの呼び出しの間にpがnullになると、nullポインタを間接的に使用しています。これが最初にshared_ptrを使用する理由です。私は実際に競争状態を解決していることを確認したいだけで、単に他の場所に移動するだけではありません。

(それは質問のポイントではないですが、このコードは一見違って見えるかもしれません。Tの動作は冪等である。pはその仕事を終えた後は、それはもはや必要ではないです。)

template< class T > 
class example 
{ 
    shared_ptr<T> p ; 
public: 
    example() 
     : p(make_shared(T())) 
    {} 
    void f() 
    { 
     shared_ptr<T> p_transient(p) ; // *1 
     if (p_transient && p_transient -> some_predicate()) 
     { 
      p.reset() ; // *2 
     } 
    } 
}; 

(* 1)と(* 2)が同時に実行されたとします。私はレースの2つの可能な結果を​​考えることができます。 (コードは、これら2例それぞれで正しいです。)私の質問は、これらがのみ例であるかどうかをされています

  • コピーがreset前に有効になりますので、p_transientは生きているTのメンバーのインスタンスを保持します。 fが返ったときに、スレッド* 1で実行されます。
  • resetがコピーの前に効力を発するので、p_transientは空に初期化されます(Tの冪等が有効になります)。デリータは、スレッド* 2で動作し、resetが返されます。

私はここで何も得られていないと感じることができないので、質問を書くことにしました。私が行方不明のものは何ですか?


P.S.ここに私が欠けていたものがあります。 shared_ptrは特別ではありません。どういうわけか、おそらく前にスマートポインタ(あまりにも多くの時間)を実装したからかもしれないと思っていました。共有ポインタは、特に弱いポインタがある場合は、(隠された)共有状態のためのmutex保護が必要です。私は保護がオブジェクト全体を包含していなければならないと考えましたが、そうではありません。

標準への参照のためのレスポンダーに感謝します。データ競合が未定義の動作になるという一般的なルールは、1.10/27 "マルチスレッド実行とデータ競合[intro.multithread]"です。特に、このような状況で事後条件が違反する可能性があることを意味します。

答えて

1

あなたが見ているのはデータ競争です。あるスレッドがあるデータに書き込むことができ、別のスレッドがそのデータを読み書きすることができる時はいつでも、それはデータ競争として知られている。

データレースはです。定義されていない動作です。これは、何が起こる可能性に制限がないことを意味します。私はブログのエントリBenign race cases: what could possibly go wrong?でこれらのことを誓います。彼はになる可能性のあるもののリストを調べます。

たとえば、メモリ位置に書き込むと、実際にはこのメモリ空間を使用してスペルレジスタを保持することができます。頻繁に起こることはありませんが、起こる可能性があります。上記のブログは、この形式のデータ競争が意図せずに核ミサイルを発射する極端な例を示しています!

2つのスレッドが1つのデータとやりとりしたい場合は、データ競合を防止する必要があります(この場合、実際の核ミサイル発射コンピュータはもう少し頑強です)。これは通常mutexやatomicsで行われます。

+0

私は一般的なケースに精通しています。問題は 'std :: shared_ptr'が提供する特定の保証についてです。 – eh9

+0

@ eh9 ahh。 'std :: shared_ptr'は保証を提供しないので、私は一般的なケースに行きました。 2.7.2.2第4節では、shared_ptr( "メンバー関数はshared_ptr' ...オブジェクトそのものだけを参照し、それらが参照するオブジェクトにはアクセスして変更しなければなりません)でデータ競争をどのように扱うべきかを述べています")、 'reset'は20.7.2.2.4のオブジェクト。 –

+0

引用されたブログエントリは便利でした。ありがとう。 – eh9

2

#1と#2を同時に実行するには、2つの異なるスレッドでexample::fを呼び出す必要があります。これらが異なるexampleインスタンスにある場合は、example::pも異なるインスタンスになるため、問題はありません。

同じexampleインスタンスの場合、標準ライブラリ競合条件に関するC++の一般規則に違反しています。別のオブジェクトインスタンスにアクセスすると、あなたは競合状態から解放されることが保証されます(通常は)。したがって、2つの異なるスレッドでの2つの異なるpush_backにアクセスできますが、と同じvectorではありません。

shared_ptrは、この同じ保証を提供します。 2つの異なるスレッドから同じshared_ptrインスタンスにアクセスしようとしていない限り、問題ありません。一度それをすると、すべての賭けはオフになります。

atomic shared_ptr functionsは、異なるスレッドから同じオブジェクトをアトミックに操作したい場合に使用します。例えば

また
shared_ptr<T> p_transient(atomic_load(&p)); 
if (p_transient && p_transient -> some_predicate()) 
{ 
    atomic_store(&p, shared_ptr<T>()); 
} 

、あなただけのミューテックスやsomesuchでfをラップすることができます。また、可能性のある破壊もmutexでラップされているので、shared_ptrを使用する必要はありません。

+0

これは、複数のインスタンスではなく、1つのインスタンスに対するものであることを明確にしました。 – eh9

+0

ここで、指定された図書館競争に関する一般的な規則はありますか?私は 'shared_ptr'の事後条件を調べるために標準を抜き出しましたが、正確な言葉遣いを見つける場所はわかりません。 – eh9

+0

@ eh9:一般的なルールは[res.on.data.races]で述べられています。 [util.smartpointer.shared]/4は、データレースに関するshared_ptr特有の動作を説明しています。 –

関連する問題