2011-07-06 305 views
8

我正在研究一個多線程程序,但有一個UI組件,它廣泛使用std :: shared_ptr來管理元素。我可以保證只有一個線程會使用這些shared_ptrs。創建一個非線程安全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的開銷非常低。我簡要介紹了這一點,以便找到我的一個難點。在我的AMD64 Phenom上調用一個函數,複製shared_ptr大約需要12ns,而在1ns左右用正常指針調用相同的函數。

考慮到這些數字,很難想象您會在原始指針和shared_ptr之間獲得任何「安全」變體。所以在這種情況下,我要麼通過實際指針,要麼將const &傳遞給shared_ptr。通常我會在整段代碼上加上一個互斥鎖,以保證在整個持續時間內保持shared_ptr。您可能會手動滾動單線程引用計數,但如果您知道它不被共享,那麼該怎麼辦?

但仔細考慮時間。除非您每秒拷貝shared_ptr數千甚至數萬次,否則您將不會注意到shared_ptr的開銷。

在同一個項目的GUI代碼中,我總是使用shared_ptr,只有服務器代碼可以在幾個關鍵區域避免它。 GUI中還有很多其他的東西讓它變慢:避免shared_ptr不會有明顯的區別。

+0

是的,我確實有一種情況,成千上萬的shared_ptrs可能會被複制,因此有興趣去除線程安全 – mpipe3

1

我建議與升壓入侵智能指針。

還有一個從斯科特·邁爾(這裏:http://www.aristeia.com/BookErrata/M29Source.html)的執行情況發表在「More Effective C++

然而,如果有幫助,我猛地一個簡單的引用計數指針(與 多態分配 和一些支持定製deletors)。 這一個決定了無線讀取

注意:我誤解了這一點。多態性的作用在antoher項目中有所變化。我也有,但它不支持定製deletor :)讓我知道是否有人感興趣;當然,它配備了獨立的單元測試功能

它配備了單元測試(例如檢查著名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

這不是一個構建時間的事情嗎?即用非線程安全參考計數來構建boost庫。我需要在同一代碼庫中的其他地方進行線程安全ref計數。 – mpipe3

3

安德烈Alexandrescu的談論在CppCon 2014

查看視頻here

而且幻燈片here

我真的想實現自己的單線程共享的指針類(有一些額外的優化)標準或提升應該提供一個模板參數,用於在他們的共享ptrs中使用原子參考計數雖然...

相關問題