2011-07-06 8 views
8

私はマルチスレッドプログラムで作業していますが、要素を管理するためにstd :: shared_ptrを大いに利用するUIコンポーネントがあります。私は、これらのshared_ptrsを1つのスレッドしか使用しないことを保証できます。スレッドセーフではないshared_ptrを作成する

スレッドセーフな参照カウントのオーバーヘッドが発生しないshared_ptrを定義する方法はありますか?

boost :: shared_ptrまたはstd :: shared_ptrに基づいている可能性があります。

編集:intrusive_ptrについてのお返事ありがとうございます。私はweak_ptrの機能も必要としているので、それを排除することは言及していません。

+3

は本当に悪いですか? – Anycorn

+2

合意。オーバーヘッドは非常に小さく、ボトルネックにはならない。どこかにボトルネックがある場合は、shared_ptrにはありません。 – inestical

+1

@inestical:この場合はおそらく正しいですが、shared_ptrがヒープにrefcountを割り当てることを忘れないでください。何百万回も行うと、重大なオーバーヘッドが発生する可能性があります。例えば、http://stackoverflow.com/questions/3628081/shared-ptr-horrible-speed – stijn

答えて

3

intrusive_ptrを使用すると、独自の参照カウントを提供できます。その参照カウントが変数の単純なインクリメント/デクリメントの場合、おそらくそれ以上のパフォーマンスは得られません。

+0

ありがとうございますが、weak_ptr機能も必要です – mpipe3

1

私はshared_ptrのコピーのオーバーヘッドが問題になっているコードを持ち、その時点で代替技術を使用しています。最初に、shared_ptrのオーバーヘッドが非常に低い他のコメントが正しいことを認定します。私はこれを実際に私のトラブルポイントの1つを見つけるためにプロファイリングしました。私のAMD64 Phenomでは、shared_ptrをコピーする関数を呼び出すのに約1nsの通常のポインタで同じ関数を呼び出すのに対して、約12nsかかる。

これらの数字を念頭に置いて、生ポインタとshared_ptrの間に「安全」な変形があるとは想像もつきません。だから私はこの場合、実際のポインタかconst &shared_ptrに渡します。通常は、コードのセクション全体にmutexロックを設定して、shared_ptrを期間全体にわたって維持することが保証されています。 シングルスレッドリファレンスカウントをハンドリングすることはできますが、共有されていないことが分かっている場合はどうなりますか?

しかし、タイミングを非常に慎重に検討してください。 shared_ptr何千も、あるいは何千万もの秒数をコピーしない限り、shared_ptrのオーバーヘッドに気付かないでしょう。

私は常にshared_ptrを使用する同じプロジェクトのGUIコードでは、サーバーコードだけがいくつかの重要な領域でそれを回避します。 GUIには他の多くのものがありますので、遅くなる:shared_ptrを避けても、大きな違いはありません。

+0

はい、何千ものshared_ptrsがスレッドセーフを削除するためにコピーされる可能性があります。 – mpipe3

1

ブースト侵入スマートポインタを使うことをお勧めします。

スコット・マイヤー(ここでは:http://www.aristeia.com/BookErrata/M29Source.html)から実装もあり「More Effective C++

に発表されたようしかし、場合には、それは私が多型の割り当てため いくつかの支援を受けて(簡単な参照カウントポインタをヤンクして、助けカスタム削除者)。 これは、スレッド非対応のと判断されます。

注:私はそれを忘れてしまった。多形性の遺伝子座は、葯のプロジェクトのバリエーションに含まれていました。私もそれを持っていますが、それはカスタムの削除をサポートしていません:)誰にも興味があれば教えてください。もちろん、それはそれは、ユニットテスト(例えば有名なremove linked list node発注のバグをチェックするために)が付属して機能

のための独立したユニットテストが付属しています。だから、あなたは何を得る:)

/* 
* counted_ptr - simple reference counted pointer. 
* 
* The is a non-intrusive implementation that allocates an additional 
* int and pointer for every counted object. 
*/ 
#ifndef COUNTED_PTR_H 
#define COUNTED_PTR_H 

#include <stdlib.h> 

extern "C" bool mtx_unit_test_countedptr(); 

namespace MtxChess { 

/* For ANSI-challenged compilers, you may want to #define 
* NO_MEMBER_TEMPLATES or explicit */ 

template <class X> 
    struct FreeMallocPolicy 
    { 
     static void do_free(X* p) { if (p) ::free(p); p = 0; } 
    }; 

template <class X> 
    struct ScalarDeletePolicy 
    { 
     static void do_free(X* p) { if (p) delete p; p = 0; } 
    }; 

template <class X> 
    struct ArrayDeletePolicy 
    { 
     static void do_free(X* p) { if (p) delete[] p; p = 0; } 
    }; 

template <class X,class _P=ScalarDeletePolicy<X> > class counted_ptr 
{ 
public: 
    typedef X element_type; 

    explicit counted_ptr(X* p = 0) // allocate a new counter 
     : itsCounter(0) {if (p) itsCounter = new counter(p);} 
    ~counted_ptr() 
     {release();} 
    counted_ptr(const counted_ptr& r) throw() 
     {acquire(r.itsCounter);} 
    operator bool() const { return 0!=get(); } 
    void clear() { (*this) = counted_ptr<X>(0); } 
    counted_ptr& operator=(const counted_ptr& r) 
    { 
     if (this != &r) { 
      auto_release keep(itsCounter); 
      acquire(r.itsCounter); 
     } 
     return *this; 
    } 
    bool operator<(const counted_ptr& r) const 
    { 
     return get()<r.get(); 
    } 
    bool operator==(const counted_ptr& r) const 
    { 
     return get()==r.get(); 
    } 
    bool operator!=(const counted_ptr& r) const 
    { 
     return get()!=r.get(); 
    } 

#ifndef NO_MEMBER_TEMPLATES 
// template <class Y> friend class counted_ptr<Y>; 
    template <class Y> counted_ptr(const counted_ptr<Y>& r) throw() 
     {acquire(r.itsCounter);} 
    template <class Y> counted_ptr& operator=(const counted_ptr<Y>& r) 
    { 
     if (this != &r) { 
      auto_release keep(itsCounter); 
      acquire(r.itsCounter); 
     } 
     return *this; 
    } 
    template <class Y> bool operator<(const counted_ptr<Y>& r) const 
    { 
     return get()<r.get(); 
    } 
    template <class Y> bool operator==(const counted_ptr<Y>& r) const 
    { 
     return get()==r.get(); 
    } 
    template <class Y> bool operator!=(const counted_ptr<Y>& r) const 
    { 
     return get()!=r.get(); 
    } 
#endif // NO_MEMBER_TEMPLATES 

    X& operator*() const throw() {return *itsCounter->ptr;} 
    X* operator->() const throw() {return itsCounter->ptr;} 
    X* get()  const throw() {return itsCounter ? itsCounter->ptr : 0;} 
    bool unique() const throw() 
     {return (itsCounter ? itsCounter->count == 1 : true);} 

private: 
    struct counter { 
     counter(X* p = 0, unsigned c = 1) : ptr(p), count(c) {} 
     X*   ptr; 
     unsigned count; 
    }* itsCounter; 

    void acquire(counter* c) throw() 
    { 
     // increment the count 
     itsCounter = c; 
     if (c) ++c->count; 
    } 

    void release() 
    { 
     dorelease(itsCounter); 
    } 

    struct auto_release 
    { 
     auto_release(counter* c) : _c(c) {} 
     ~auto_release() { dorelease(_c); } 
     counter* _c; 
    }; 

    void static dorelease(counter* itsCounter) 
    { 
     // decrement the count, delete if it is 0 
     if (itsCounter) { 
      if (--itsCounter->count == 0) { 
       _P::do_free(itsCounter->ptr); 
       delete itsCounter; 
      } 
      itsCounter = 0; 
     } 
    } 
}; 

} // EON 

#endif // COUNTED_PTR_H 

ユニットテストを知っている

/* 
* counted_ptr (cpp) - simple reference counted pointer. 
* 
* The is a non-intrusive implementation that allocates an additional 
* int and pointer for every counted object. 
*/ 

#include "counted_ptr.hpp" 
#include "internal.hpp" 
#include <map> 
#include <string> 

namespace MtxChess { 

    namespace /*anon*/ 
    { 
     // sensed events 
     typedef std::map<std::string, int> Events; 
     static Events constructions, destructions; 

     struct Trackable 
     { 
      Trackable(const std::string& id) : _id(id) { constructions[_id]++; } 
      ~Trackable()        { destructions[_id]++; } 
      const std::string _id; 
     }; 

     typedef counted_ptr<Trackable> target_t; 

     bool testBehaviour() 
     { 
      static const counted_ptr<Trackable> Nil = target_t(0); 
      bool ok = true; 

      constructions.clear(); 
      destructions.clear(); 

      MTXASSERT_EQ(ok, 0ul, constructions.size()); 
      MTXASSERT_EQ(ok, 0ul, destructions.size()); 

      target_t a = target_t(new Trackable("aap")); 

      MTXASSERT_EQ(ok, 1ul, constructions.size()); 
      MTXASSERT_EQ(ok, 1, constructions["aap"]); 
      MTXASSERT_EQ(ok, 0ul, destructions.size()); 

      MTXASSERT_EQ(ok, 0, constructions["noot"]); 
      MTXASSERT_EQ(ok, 2ul, constructions.size()); 
      MTXASSERT_EQ(ok, 0ul, destructions.size()); 

      target_t hold; 
      { 
       target_t b = target_t(new Trackable("noot")), 
         c = target_t(new Trackable("mies")), 
         nil = Nil, 
         a2 = a; 

       MTXASSERT(ok, a2==a); 
       MTXASSERT(ok, nil!=a); 

       MTXASSERT_EQ(ok, 3ul, constructions.size()); 
       MTXASSERT_EQ(ok, 1, constructions["aap"]); 
       MTXASSERT_EQ(ok, 1, constructions["noot"]); 
       MTXASSERT_EQ(ok, 1, constructions["mies"]); 
       MTXASSERT_EQ(ok, 0, constructions["broer"]); 
       MTXASSERT_EQ(ok, 4ul, constructions.size()); 

       MTXASSERT_EQ(ok, 0ul, destructions.size()); 

       hold = b; 
      } 

      MTXASSERT_EQ(ok, 1ul, destructions.size()); 
      MTXASSERT_EQ(ok, 0, destructions["aap"]); 
      MTXASSERT_EQ(ok, 0, destructions["noot"]); 
      MTXASSERT_EQ(ok, 1, destructions["mies"]); 
      MTXASSERT_EQ(ok, 3ul, destructions.size()); 

      hold = Nil; 
      MTXASSERT_EQ(ok, 3ul, destructions.size()); 
      MTXASSERT_EQ(ok, 0, destructions["aap"]); 
      MTXASSERT_EQ(ok, 1, destructions["noot"]); 
      MTXASSERT_EQ(ok, 1, destructions["mies"]); 
      MTXASSERT_EQ(ok, 4ul, constructions.size()); 

      // ok, enuf for now 
      return ok; 
     } 

     struct Linked : Trackable 
     { 
      Linked(const std::string&t):Trackable(t){} 
      counted_ptr<Linked> next; 
     }; 

     bool testLinked() 
     { 
      bool ok = true; 

      constructions.clear(); 
      destructions.clear(); 
      MTXASSERT_EQ(ok, 0ul, constructions.size()); 
      MTXASSERT_EQ(ok, 0ul, destructions.size()); 

      counted_ptr<Linked> node(new Linked("parent")); 
      MTXASSERT(ok, node.get()); 
      node->next = counted_ptr<Linked>(new Linked("child")); 

      MTXASSERT_EQ(ok, 2ul, constructions.size()); 
      MTXASSERT_EQ(ok, 0ul, destructions.size()); 

      node = node->next; 
      MTXASSERT(ok, node.get()); 

      MTXASSERT_EQ(ok, 2ul, constructions.size()); 
      MTXASSERT_EQ(ok, 1ul, destructions.size()); 

      node = node->next; 
      MTXASSERT(ok,!node.get()); 

      MTXASSERT_EQ(ok, 2ul, constructions.size()); 
      MTXASSERT_EQ(ok, 2ul, destructions.size()); 

      return ok; 
     } 

    } 

} // EON 

int main() 
{ 
    using namespace MtxChess; 

    bool ok = true; 
    ok = testBehaviour() && ok; 
    ok = testLinked() && ok; 

    return ok?0:1; 
} 
+0

ありがとうございますが、weak_ptr機能も必要です – mpipe3

+0

どのように侵入型スマートポインタを使用しますかweak_ptrs? – mpipe3

0

ブーストを使用すると、スレッドセーフな参照カウントを使用しないことを定義することができるマクロを提供(スタンドアロンとしてコンパイルされます)。

+2

それはビルド時間のことではありませんか?つまり、スレッドセーフでないリファレンスカウントでブーストライブラリを構築する。私は同じコードベースのどこかでスレッドセーフなリファレンスカウントを必要とします。 – mpipe3

3

アンドレイアレキは2014

here

そしてスライドがhere

私は本当に考えて、ビデオをすべて見るCppConで(いくつかの追加の最適化で)自分自身のシングルスレッドの共有ポインタクラスを実装について話しました標準またはブーストは、共有のptrsで原子参照カウントを使用するためのテンプレートパラメータを提供する必要があります...

関連する問題