2012-04-22 13 views
18

私は侵入共有ポインタを作成していますが、参照カウンタとしてC++ 11 <atomic>を使用しています。ここに私のコードの関連フラグメントは、次のとおりです。C++ 11アトミックと侵入共有ポインタ参照カウント

//... 
mutable std::atomic<unsigned> count; 
//... 

void 
SharedObject::addReference() const 
{ 
    std::atomic_fetch_add_explicit (&count, 1u, 
     std::memory_order_consume); 
} 

void 
SharedObject::removeReference() const 
{ 
    bool destroy; 

    destroy = std::atomic_fetch_sub_explicit (&count, 1u, 
     std::memory_order_consume) == 1; 

    if (destroy) 
     delete this; 
} 

私はmemory_order_acquirememory_order_release最初に開始しているが、その後、私はmemory_order_consumeが十分であることを自分自身を確信させました。さらなる審議の末、memory_order_relaxedでも動作するはずです。

問題は、操作にmemory_order_consumeを使用することができますか、弱い発注(memory_order_relaxed)を使用することができますか、より厳密な発注を使用する必要がありますか?

+0

カウンタは基本的に 'delete'文の再帰的ロックとして機能するので、' removeReference'の 'addReference'と' releaseReference'の 'acquire'は正しい順序です。しかし、あなたの 'addReference'は、カウンタがゼロでないことを確認しなければなりません! –

+0

@KerrekSB:オブジェクトは、 'SharedPtr <>'に代入される前にオブジェクトが最初に作成された後、 'addReference()'でカウンタがゼロになります。取得/解放のセマンティクスは、常に有効であるようです。しかし、より弱い順序制約を使用することはできませんし、なぜそうではありませんか? – wilx

+0

ゼロについて:refcountが1であると仮定します。スレッド1はオブジェクトを削除したいので、減算を呼び出します。この時点でスレッド2がスレッド数を増やしたい場合、0に1をインクリメントしますが、スレッド1は先に進み、とにかくオブジェクトを削除します。それは避けるべきです。 –

答えて

20
void 
SharedObject::addReference() const 
{ 
    std::atomic_fetch_add_explicit (&count, 1u, std::memory_order_relaxed); 
} 

void 
SharedObject::removeReference() const 
{ 
    if (std::atomic_fetch_sub_explicit (&count, 1u, std::memory_order_release) == 1) { 
     std::atomic_thread_fence(boost::memory_order_acquire); 
     delete this; 
    } 
} 

あなたはdeletefetch_sub後、厳密であることをatomic_thread_fenceなどを使用します。リンクされたテキストからReference

引用:リファレンスカウンタを増やす

は常にmemory_order_relaxed で行うことができます。オブジェクトへの新しい参照は、既存の参照から を形成し、そしてから既存の参照を渡すことができます別のスレッドへの1つの スレッドは、必要な同期をすでに提供している必要があります。

スレッド内のオブジェクトへの可能なアクセスを、既存の参照を介して実行して、別のスレッドの オブジェクトを削除する前に実行することが重要です。これは、オブジェクトを削除する前に参照を削除した後に "解放" 操作によって達成されます(この参照は明らかに以前に発生していたはずです)。

はfetch_sub 動作のためmemory_order_acq_relを使用することも可能であるが、 参照カウンタがまだゼロに達していないと性能 ペナルティを課すことができる場合、これは不要な「取得」操作になります。

+0

私はすでに答えを受け入れていて、そのようにポインタを実装している間、引用符で私はちょっと考えましたが、もう一つの質問です。 'memory_order_relaxed'への減少を緩和し、' if() 'の真の分岐を作ることは' memory_order_acq_rel'を使うのはどうでしょうか? – wilx

+0

あなたの '_relaxed'、' _acq_rel'には 'fetch_sub(_relaxed)'と 'fence(_acq_rel)'と 'delete'の厳密な順序はありません。この[ページ](http://www.chaoticmind.net/~hcb/projects/boost.atomic/doc/atomic/thread_coordination.html)に興味があるかもしれません。 – user2k5

+0

さて、ページを読んだ後、私はそれが 'fetch_sub(_relaxed)'になることはできないと思います。これは、 '_relaxed'演算が他の演算に対してまったく順序付けされていないためです。しかし、 'fetch_sub(_consume)'と 'fence(_acq_rel)'はどうでしょうか? 'fetch_sub(_consume)'はコンパイラの並べ替えの障壁となり、 'fence(_acq_rel)'は全てのロードとストアを残りのマシンに対して命令し、次の 'delete'はメモリを一貫して見せます。または私は間違っていますか? – wilx

関連する問題