std :: shared_ptrスレッドセーフティの説明


106

私はhttp://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.htmlを読んでいますが、いくつかのスレッドセーフティの問題はまだわかりません:

  1. 標準では、参照カウントがスレッドセーフで処理され、プラットフォームに依存しないことが保証されています。
  2. 同様の問題-標準では、1つのスレッド(最後の参照を保持している)のみが共有オブジェクトで削除を呼び出すことが保証されていますよね?
  3. shared_ptrは、それに格納されているオブジェクトのスレッドセーフを保証しませんか?

編集:

疑似コード:

// Thread I
shared_ptr<A> a (new A (1));

// Thread II
shared_ptr<A> b (a);

// Thread III
shared_ptr<A> c (a);

// Thread IV
shared_ptr<A> d (a);

d.reset (new A (10));

スレッドIVでreset()を呼び出すと、最初のスレッドで作成されたAクラスの以前のインスタンスが削除され、新しいインスタンスに置き換えられますか?さらに、IVスレッドでreset()を呼び出した後、他のスレッドには新しく作成されたオブジェクトのみが表示されますか?


24
右、右、そして右。
散布

16
make_shared代わりに使用してくださいnew
qdii

回答:


87

他の人が指摘したように、元の3つの質問に関して正しく理解されています。

しかし、編集の最後の部分

スレッドIVでreset()を呼び出すと、最初のスレッドで作成されたAクラスの以前のインスタンスが削除され、新しいインスタンスに置き換えられますか?さらに、IVスレッドでreset()を呼び出した後、他のスレッドには新しく作成されたオブジェクトのみが表示されますか?

間違っています。のみd新規作成]をポイントしますA(10)、とabと、c元にポイントしていきますA(1)。これは、次の短い例ではっきりと見ることができます。

#include <memory>
#include <iostream>
using namespace std;

struct A
{
  int a;
  A(int a) : a(a) {}
};

int main(int argc, char **argv)
{
  shared_ptr<A> a(new A(1));
  shared_ptr<A> b(a), c(a), d(a);

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  d.reset(new A(10));

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;
                                                                                                                 
  return 0;                                                                                                          
}

(明らかに、私はスレッドを気にしませんでした:それは shared_ptr::reset()動作に。)

このコードの出力は

a:1 b:1 c:1 d:1

a:1 b:1 c:1 d:10


35
  1. 正解ですshared_ptr。sは、参照カウント値のアトミックな増分/減分を使用します。

  2. 標準では、1つのスレッドのみが共有オブジェクトの削除演算子を呼び出すことが保証されています。共有ポインターのコピーを削除する最後のスレッドが、deleteを呼び出すスレッドになることを具体的に指定しているかどうかはわかりません(実際にはそうなります)。

  3. いいえ、そうではありません。それに格納されているオブジェクトは、複数のスレッドで同時に編集できます。

編集:わずかなフォローアップですが、共有ポインタの一般的な仕組みについて知りたい場合は、boost::shared_ptrソースを確認することをお勧めします。 http //www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp


3
1.「 'shared_ptrs'は、参照カウント値のアトミックインクリメント/デクリメントを使用する」と言います。それらは、コンテキストの切り替えを行うアトミックなインクリメント/デクリメントに内部ロックを使用しないことを意味しますか?単純な言語では、ロックを使用せずに複数のスレッドが参照カウントを増減できますか?アトミック増分は、特別なatomic_test_and_swap / atomic_test_and_increment命令によって行われますか?
rahul.deshmukhpatil 2015

@rahulコンパイラはミューテックス/ロックを自由に使用できますが、ほとんどの優れたコンパイラは、ロックフリーで実行できるプラットフォームではミューテックス/ロックを使用しません。
バーナード

@バーナード:それはプラットフォームの「コンパイラstd lib shared_ptr」実装に依存しているということですか?
rahul.deshmukhpatil 2017

2
はい。私の理解から、規格はロックフリーでなければならないことを述べていません。しかし、最新のGCCとMSVCでは、Intel x86ハードウェアでロックフリーであり、ハードウェアがサポートしている場合、他の優れたコンパイラーも同じようにする可能性が高いと思います。
バーナード

18

std::shared_ptr スレッドセーフではありません。

共有ポインターは、オブジェクトへのポインターと制御ブロックへのポインターの2つのポインターのペアです(参照カウンターを保持し、弱いポインターにリンクします...)。

複数のstd :: shared_ptrが存在する可能性があり、参照カウンターを変更するために制御ブロックにアクセスする場合は常にスレッドセーフですが、 std::shared_ptrそれ自体はスレッドセーフでもアトミックでもありません。

std::shared_ptr別のスレッドが新しいオブジェクトを使用している間に新しいオブジェクトを割り当てると、新しいオブジェクトポインターが表示される可能性がありますが、古いオブジェクトの制御ブロックへのポインターが引き続き使用されます=> CRASH。


4
単一のstd::shared_ptrインスタンスはスレッドセーフではないと言えます。std :: shared_ptr参照から:If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;
JKovalsky

これはもっといい言葉になるかもしれません。std::shared_ptr<T>常に使用される場合、インスタンスは、スレッドセーフが保証されている値によってスレッドの境界を越えて(移動/コピー)。他のすべての使用は、std::shared_ptr<T>&スレッドの境界を越えて安全ではありません
WhiZTiM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.