2009-07-16 9 views
6

現在IVEを参照カウントは、使用して、いくつかの参照カウントのクラスを持って、次のC++:マルチスレッドと

class RefCounted 
{ 
public: 
    void IncRef() 
    { 
     ++refCnt; 
    } 
    void DecRef() 
    { 
     if(!--refCnt)delete this; 
    } 
protected: 
    RefCounted():refCnt(0){} 
private: 
    unsigned refCnt; 
    //not implemented 
    RefCounted(RefCounted&); 
    RefCounted& operator = (RefCounted&}; 
}; 

は、私もそのは一様に(例えば、1つで使用されていない全てが、参照カウントを処理するスマートポインタのクラスを持っていますまたはパフォーマンスクリティカルなコードの2ビット、ここではIncRefとDecRef呼び出しの数を最小限に抑えました)。私は、リーダ/ライタロックシステムを使用する予定のクラス自体の一般的な使用のために

template<class T>class RefCountedPtr 
{ 
public: 
    RefCountedPtr(T *p) 
    :p(p) 
    { 
     if(p)p->IncRef(); 
    } 
    ~RefCountedPtr() 
    { 
     if(p)p->DecRef(); 
    } 
    RefCountedPtr<T>& operator = (T *newP) 
    { 
     if(newP)newP->IncRef(); 
     if(p) p ->DecRef(); 
     p = newP; 
     return *this; 
    } 
    RefCountedPtr<T>& operator = (RefCountedPtr<T> &newP) 
    { 
     if(newP.p)newP.p->IncRef(); 
     if(p)  p  ->DecRef(); 
     p = newP.p; 
     return *this; 
    } 
    T& operator *() 
    { 
     return *p; 
    } 
    T* operator ->() 
    { 
     return p; 
    } 
    //comparison operators etc and some const versions of the above... 
private: 
    T *p; 
}; 

、しかし、私は本当にすべての単一IncRefとDecRefコールのための書き込みロックを取得する必要がありますする必要はいけません。

私もちょうど検討し、ポインタがちょうどIncRef呼び出しの前に無効にすることができるシナリオを考えた:

class Texture : public RefCounted 
{ 
public: 
    //...various operations... 
private: 
    Texture(const std::string &file) 
    { 
     //...load texture from file... 
     TexPool.insert(this); 
    } 
    virtual ~Texture() 
    { 
     TexPool.erase(this); 
    } 
    freind CreateTextureFromFile; 
}; 
Texture *CreateTexture(const std::string &file) 
{ 
    TexPoolIterator i = TexPool.find(file); 
    if(i != TexPool.end())return *i; 
    else return new Texture(file); 
} 
 
ThreadA        ThreadB 
t = CreateTexture("ball.png"); 
t->IncRef(); 
...use t...       t2 = CreateTexture("ball.png");//returns *t 
...         thread suspended... 
t->DecRef();//deletes t    ... 
...         t2->IncRef();//ERROR 

だから私は、私は完全に参照カウントモデルを変更する必要があると思い、理由デザインのリターンは、以下のようなものをサポートするためにした後、私は、参考文献を追加しました:

MyObj->GetSomething()->GetSomethingElse()->DoSomething(); 

ではなくなる:

SomeObject a = MyObj->GetSomething(); 
AnotherObject *b = a->GetSomethingElse(); 
b->DoSomething(); 
b->DecRef(); 
a->DecRef(); 

マルチスレッド環境で、C++で高速参照カウントを行うためのきれいな方法はありますか?

+9

http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/shared_ptrを使ってください。 htm – avakar

+1

また、一部のコンパイラではstd :: tr1 :: shared_ptrとして利用できます – bdonlan

答えて

16

参照カウントをアトミックにすると、ロックは必要ありません。 Windowsでは:: InterlockedIncrementと:: InterlockedDecrementを使用できます。 C++ 0xでは、あなたはアトミック<>を持っています。

+0

あるいは、潜在的な変更を回避するためにその場所のクリティカルセクションに失敗します。 – Goz

+3

InterlockedIncrementはクリティカルセクションよりもはるかに高速で、win32 APIで利用できます。 –

+2

これはレースを修正しません。最後の参照は、APIを指定してCreateTextureとAddRefの間で削除することができます。 – bdonlan

2

osg、OpenSceneGraphはこのような構造を持っています。

あなたはosg :: Referencedからクラスを派生させ、マルチスレッドでもデストラクタは気にしません。

あなただけのようなクラスを作成します。あなたはそれは私がちょうどそれはしかし、そこに非常に高速であるboost::shared_ptr

を使用したい特定のボトルネックであることを知っている場合を除き

MyClass* m = new MyClass(); 
10

osg::ref_ptr<MyClass> m = new MyClass(); 

の代わりに、割り当てられている余分なコントロールブロックの余分なオーバーヘッドです。一方で、それは多くの利点があります。

  • あなたが、それは実際にものが
  • 成し遂げるためにあなたの時間を残してのあなたの精神的なサイクルを無駄にする必要はありません
  • 正しい
  • ポータブルでありますそれは速いです
  • それは業界標準であり、他のプログラマーはすぐにそれを理解します。
  • それはあなたがいない場合は、注意してください。また

であるべきboostを使用するためにあなたを強制的に、あなたはおそらく、参照カウントオブジェクトのリーダー\ライタロックを望んでいます。競合は最小限に抑えられ、余分なオーバーヘッドはあなたの持つ利益を完全に圧倒します。共有ポインタはチップレベルアトミックint演算で実装されますが、これはリーダ/ライタロックよりもはるかに高速な通常のミューテックスよりもかなり優れています。

+0

boost :: shared_ptrにはロックフリーの実装があり、BOOST_SP_NO_ATOMIC_ACCESSで構築されていない限り、マルチスレッドアプリケーションに適しています。 –

+0

私は明示的にロックを望んでいません参照カウントは、データを渡すだけでロックを解除することがたくさんあるので、いくつかのスレッドに渡って内容が操作されるオブジェクトについて話していました:) –

+0

boost :: shared_ptrはレースの問題をどのように修正しますか?オブジェクトが作成された後もまだ何かが起こっていますが、現在どこでも使用されていないテクスチャをクリアする必要があるため、プールに参照を保持したくないので、キャッシュされたテクスチャと新しい参照が作成され、破壊される可能性がありますか? –

1

boost :: shared_ptrとPoco :: SharedPtrは、両方とも自立スマートポインタでこのイディオムをラップします。

上記の例のように、侵入的な参照カウントが必要な場合、PocoのAutoPtrはうまく機能します。

編集:私はリンクを追加していましたが、評判は低すぎました。どのクラス名でもGoogleを使うことができます。

1

主な問題は、CreateTextureが返される前に参照を取得していないことです。

// PSEUDOCODE WARNING: --refcnt MUST be replaced by an atomic decrement-and-test 
// Likewise, AddRef() MUST use an atomic increment. 
void DecRef() { 
    if (!--refcnt) { 
     lock(); 
     if (!refcnt) 
      delete this; 
     unlock(); 
    } 
} 

と::

Texture *CreateTexture(const std::string &file) 
{ 
    lock(); 

    TexPoolIterator i = TexPool.find(file); 
    if(i != TexPool.end()) { 
     *i->AddRef(); 
     unlock(); 
     return *i; 
    } 
    unlock(); 
    return new Texture(file); 
} 
あなたは、このようなオープン・コーディングしている場合、それを処理するための最も簡単な方法は、削除する前に参照を解放するときもそうのように、取られTexPool周りのロックを持っていることです

他の人が触れたように、boost :: shared_ptr(別名std :: tr1 :: shared_ptr)はこれをすべてロックレスで安全に実装しており、テクスチャキャッシュに役立つウィークポインタもサポートしています。

6

ブーストまたはC++ 0Xを使用しないが、依然としてロックレス参照カウントが必要な場合は、正しいプラットフォーム固有のアトミックインクリメント/アトミックデクリメントアセンブリルーチンをコードに組み込むことで対応できます。たとえば、リファレンスカウントに使用するAtomicCounterクラスを次に示します。それは、最も一般的なOSの者の下で働く:はい、それは#ifdefsの厄介な混乱

https://public.msli.com/lcs/muscle/html/AtomicCounter_8h_source.html

です。しかし、それは動作します。

1

キャッシュにはboost::weak_ptrまたはそれに類似した構造を使用する必要があります。

0

私はあなたがこの特定のデザインのクリティカルセクションを必要とすると思います。必要な箇所の1つはCreateTextureです。そうしないと、システム内に複数の同一のテクスチャオブジェクトがある可能性があります。そして、一般的に、複数のスレッドが同じテクスチャを作成して破壊することができれば、それは「変更可能な共有状態」になります。

2

スレッドセーフかアトミックスレッドセーフなのですか? boot :: shared_ptrは単なるスレッドセーフです。あなたはまだそれを安全にコピーするためにshared_ptrを "所有"する必要があります。

ここでは原子的にスレッドセーフな参照カウントで行った実験的なものがあります。 http://atomic-ptr-plus.sourceforge.net/には、何が関係しているのかを知ることができます。

+0

これは優れた例です。何が起こっているかの概要については、http://www.drdobbs.com/cpp/184401888;jsessionid=S3LQ44DGLYBR1QE1GHPSKHWATMY32JVN?_requestid=4096も参照してください。 – mtasic85

0

は、このPDFファイルを見てみましょう:http://www.research.ibm.com/people/d/dfb/papers/Bacon01Concurrent.pdf

これは、任意のロックを必要としない参照カウントシステムが記載されています。 (ロックすると見なせるスレッドを一度に「一時停止」する必要があります)また、ガベージサイクルも収集されます。欠点は、はるかに複雑であることです。読者のための練習として残された重要なものもいくつかあります。新しいスレッドが作成されたときや、古いスレッドが削除されたとき、または本来的に非周期的なオブジェクトを扱うときのように。 (あなたがこれをすることにした場合、どうやってこれらをうろついているか教えてください)