web-dev-qa-db-ja.com

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

私は読んでいます http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html といくつかのスレッドの安全性の問題はまだ明確ではありません:

  1. 標準では、参照カウントがスレッドセーフに処理され、プラットフォームに依存しないことが保証されていますよね?
  2. 同様の問題-標準は、1つのスレッド(最後の参照を保持する)のみが共有オブジェクトでdeleteを呼び出すことを保証しますか?
  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()を呼び出した後、他のスレッドは新しく作成されたオブジェクトのみを参照しますか?

97
Goofy

他の人が指摘したように、あなたはあなたの元の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

83
Nicu Stiurca
  1. 正解、shared_ptrsは、参照カウント値のアトミックなインクリメント/デクリメントを使用します。

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

  3. いいえ、保存されているオブジェクトは複数のスレッドで同時に編集できます。

編集:わずかなフォローアップ。共有ポインターが一般的にどのように機能するかを知りたい場合は、boost::shared_ptrソース: http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp

34
NothingMore

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

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

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

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

14
Lothar