2010-11-30 1 views
3

誰でも私はCopy-on-write (COW)イディオムのスレッドセーフな実装を指摘できますか? this siteのサンプルコードはうまく見えます - それはスレッドセーフですか?COW(Copy-On-Write)イディオムのスレッドセーフ実装ですか?

私はそれをどのように使用するのだろうかと疑問に思っている人はFooクラスにstd::map<int,double>というメンバーがあります。 Fooオブジェクトは私のコードに非常に頻繁にコピーされますが、コピーはほとんど含まれていませんmapを変更します。私は、COWが、コピーコンストラクタFooでマップの内容全体をコピーするのと比較して22%のパフォーマンス向上を見出しましたが、複数のスレッドが使用されていると私のCOW実装がクラッシュします。

UPDATE:あなたはそれを求め以来

さて、ここでのコードは、最小限の例に減少し、次のとおりです。

まず、参照カウントマップ:

class RcMap {        
public: 
    typedef std::map<int,double> Container; 
    typedef Container::const_iterator const_iterator; 
    typedef Container::iterator iterator; 

    RcMap() : count_(1) {} 

    RcMap(const RcMap& other) : count_(1) { 
    m_ = other.Get(); 
    } 

    unsigned Count() const { return count_; } 
    unsigned IncCount() { return ++count_; } 
    unsigned DecCount() { 
    if(count_ > 0) --count_; 
    return count_; 
    } 
    void insert(int i, double d) { 
    m_.insert(std::make_pair(i,d)); 
    } 
    iterator begin() { return m_.begin(); } 
    iterator end() { return m_.end(); } 
    const_iterator begin() const { return m_.begin(); } 
    const_iterator end() const { return m_.end(); } 

protected: 
    const Container& Get() const { return m_; } 

private: 
    void operator=(const RcMap&); // disallow 

    Container m_; 
    unsigned count_; 
}; 

コピーライト時のメカニズムを使用して、このようなマップRcMapを含むクラスFooがあります。

class Foo { 
public: 
    Foo() : m_(NULL) {} 

    Foo(const Foo& other) : m_(other.m_) { 
    if (m_) m_->IncCount(); 
    } 

    Foo& operator= (const Foo& other) { 
    RcMap* const old = m_; 
    m_ = other.m_; 
    if(m_ != 0) 
     m_->IncCount(); 
    if (old != 0 && old->DecCount() == 0) { 
     delete old; 
    } 
    return *this; 
    } 

    virtual ~Foo() { 
    if(m_ != 0 && m_->DecCount() == 0){ 
     delete m_; 
     m_ = 0; 
    } 
    } 

    const RcMap& GetMap() const { 
    if(m_ == 0) 
     return EmptyStaticRcMap(); 
    return *m_; 
    } 

    RcMap& GetMap() { 
    if(m_ == 0) 
     m_ = new RcMap(); 
    if (m_->Count() > 1) { 
     RcMap* d = new RcMap(*m_); 
     m_->DecCount(); 
     m_ = d; 
    } 
    assert(m_->Count() == 1); 
    return *m_; 
    } 

    static const RcMap& EmptyStaticRcMap(){ 
    static const RcMap empty; 
    return empty; 
    } 

private: 
    RcMap* m_; 
}; 

私はまだこの最小限の例でクラッシュを再現できませんでしたが、コピーコンストラクタまたは代入演算子Fooを並列に使用すると、元のコードでクラッシュが発生します。しかし、誰かがスレッドセーフなバグを見つけられるかもしれません。

+0

誰でも、ここでヒントされているように、Compare-and-Swapイディオムを使用して実装する方法を説明できますか(http://en.wikipedia.org/wiki/Copy-on-write)? – Frank

+1

COWがクラッシュしている場合は、問題を示す最小限のコードサンプルを投稿できる場合に役立ちます。また、どのようなクラッシュがありますか? –

+0

さて、最小限のコードサンプルを追加しました。クラッシュは 'Foo'デストラクタの' delete m_'で起こります。 – Frank

答えて

2

RcMapスレッドセーフであるためには、参照カウントをアトミックにする必要があります。 G ++ 4.1では、これを実装するためにatomic builtinsを使用します。

+0

クラッシュを修正しました。この簡単なソリューションをありがとう! – Frank

3

COWは本質的にスレッドセーフである。なぜなら、オリジナルは本質的に不変であり、コピーを誘導するスレッドだけが、作成中のプロセスでコピーされたバージョンを見るからである。あなたは2つだけを監視する必要があります。

  1. コピーが発生している間、元が別のスレッドによって削除されませんことを確認してください。これは直交性の問題です(たとえば、スレッドセーフな参照カウントを使用するなど)。
  2. コピー中に実行するすべての読み取り操作がスレッドセーフであることを確認してください。これはめったに問題にはなりませんが、たとえば、読み取りによってキャッシュにデータが読み込まれることがあります。
    • 実際、この前提条件に違反すると、読み取り操作がスレッドセーフではなく、おそらくCOW以外のコードにも影響を与えます。あなたは可変マップを(あなたがいるように見える)コピーする場合
1

は、そのコピーが完了するまで、元のオブジェクトの参照カウントを低下させません。 (そうしないと、コピーしているオブジェクトへの書き込みを許可してスレッドの安全性を損なう可能性があります)。

さらに、完全な不変のマップ実装を使用すると、共有部分構造を使ってコピーや更新をさらに安くできます。あなたはできる。このトピックには現在回答していないprevious questionがあります。

関連する問題