2011-07-06 7 views
8

मैं एक बहु-थ्रेडेड प्रोग्राम पर काम कर रहा हूं, लेकिन एक UI घटक है जो तत्वों को प्रबंधित करने के लिए std :: shared_ptr का व्यापक उपयोग करता है। मैं गारंटी दे सकता हूं कि केवल एक धागा इन साझा_ptrs का उपयोग करेगा।एक गैर-थ्रेड सुरक्षित साझा करना__rr

क्या कोई साझा_ptr परिभाषित करने का कोई तरीका है जो थ्रेड सुरक्षित संदर्भ गणना के ओवरहेड को नहीं लेता है?

यह boost :: shared_ptr या std :: shared_ptr पर आधारित हो सकता है।

संपादित करें: intrusive_ptr का उल्लेख करने वाले उत्तरों के लिए धन्यवाद। मैंने यह उल्लेख करने के लिए उपेक्षित किया कि मुझे कमजोर_पीटीआर कार्यक्षमता की भी आवश्यकता है ताकि इसे नियंत्रित किया जा सके।

+3

क्या ओवरहेड वास्तव में खराब है? – Anycorn

+2

सहमत हुए। ओवरहेड इतना छोटा है, कि यह बाधा नहीं होनी चाहिए। यदि आपके पास कहीं बाधाएं हैं, तो यह shared_ptr में नहीं है। – inestical

+1

@inestical: इस मामले के लिए आप शायद सही हैं, लेकिन यह नहीं भूलें कि shared_ptr ढेर पर इसका पुनर्वित्त आवंटित करता है। ऐसा करने से दस लाख बार गंभीर ऊपरी हो सकता है। उदाहरण के लिए http://stackoverflow.com/questions/3628081/shared-ptr-horrible-speed – stijn

उत्तर

3

आप intrusive_ptr का उपयोग कर सकते हैं, क्योंकि यह आपको अपनी संदर्भ गणना प्रदान करने की अनुमति देता है। यदि वह संदर्भ गिनती एक चर की एक सरल वृद्धि/कमी है तो आपको उससे बेहतर प्रदर्शन नहीं मिलेगा।

+0

धन्यवाद, लेकिन मुझे कमजोर_पीटीआर कार्यक्षमता भी चाहिए – mpipe3

1

मेरे पास कोड है जहां shared_ptr की प्रतिलिपि बनाने का ओवरहेड एक मुद्दा बन गया है, और उस बिंदु पर वैकल्पिक तकनीकों का उपयोग किया है। मुझे पहले योग्यता दें कि अन्य टिप्पणियां सही हैं कि shared_ptr का ओवरहेड बहुत कम है। मैंने वास्तव में अपने मुसीबतों में से एक को खोजने के लिए इसका प्रोफाइल किया। मेरे एएमडी 64 फेनोम पर एक फ़ंक्शन को कॉल करने पर shared_ptr प्रतिलिपि बनाते हुए लगभग 12ns लेते हैं, जो एक ही फ़ंक्शन को सामान्य पॉइंटर के साथ लगभग 1ns पर कॉल करते हैं।

उन संख्याओं के साथ दिमाग में यह कल्पना करना मुश्किल है कि आपको कच्चे सूचक और shared_ptr के बीच किसी भी प्रकार का "सुरक्षित" संस्करण मिलेगा। तो मैं इन मामलों में क्या करता हूं या तो मैं वास्तविक सूचक या const & को shared_ptr पर पास करता हूं। आम तौर पर मैंने कोड के पूरे भाग पर एक म्यूटेक्स लॉक लगाया ताकि मुझे पूरी अवधि के लिए shared_ptr को बनाए रखने की गारंटी दी जा सके। आप एक-थ्रेडेड संदर्भ संख्या को हाथ से रोल कर सकते हैं, लेकिन यदि आपको पता है कि यह साझा नहीं किया जा रहा है तो क्या बिंदु होगा?

लेकिन समय पर ध्यान से विचार करें। जब तक आप shared_ptr हजारों, या यहां तक ​​कि हजारों बार प्रति सेकंड की प्रतिलिपि बना रहे हैं, तो आप shared_ptr के ओवरहेड को नोटिस नहीं करेंगे।

उसी प्रोजेक्ट पर जीयूआई कोड में मैं हमेशा एक साझा_पीटीआर का उपयोग करता हूं, केवल सर्वर कोड कुछ प्रमुख क्षेत्रों में इसे टालता है। जीयूआई में इतनी सारी चीजें हैं जो इसे धीमा करती हैं: shared_ptr से परहेज करने से कोई सराहनीय अंतर नहीं होगा।

+0

हां, मेरे पास ऐसी स्थिति है जहां थ्रेड सुरक्षा – mpipe3

1

मैं बूस्ट घुसपैठ स्मार्ट सूचक के साथ जाने का सुझाव देता हूं।

वहाँ भी स्कॉट मेयेर (यहाँ: http://www.aristeia.com/BookErrata/M29Source.html) से एक क्रियान्वयन है के रूप में 'More Effective C++'

में प्रकाशित

हालांकि, मामले में यह मदद करता है, मैं एक साधारण refcounting सूचक ( साथ बहुरूपी कार्य और के लिए कुछ समर्थन yanked कस्टम deletors)। यह निर्णय अवांछित-जागरूक का निर्णय लिया गया है।

नोट: मैंने इसे गलत समझा। Polymorphic असाइनमेंट antoher परियोजना के लिए एक भिन्नता में थे। मेरे पास भी है लेकिन यह कस्टम डिलेक्टर का समर्थन नहीं करता है :) अगर कोई दिलचस्पी लेता है तो मुझे बताएं; (उदाहरण के लिए प्रसिद्ध 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

को हटाने में रुचि में हजारों साझा_प्टर्स की प्रतिलिपि बनाई जा सकती है, लेकिन मुझे कमजोर_पीटीआर कार्यक्षमता भी चाहिए – mpipe3

+0

आप घुसपैठ स्मार्ट पॉइंटर्स का उपयोग कैसे करते हैं weak_ptrs? – mpipe3

0

बूस्ट एक मैक्रो आपको लगता है कि परिभाषित कर सकते हैं धागा सुरक्षित संदर्भ गिनती का उपयोग नहीं होगा प्रदान करता है।

+2

क्या यह एक समय की बात नहीं है? यानी गैर थ्रेड सुरक्षित रेफ गिनती के साथ बूस्ट लाइब्रेरी का निर्माण करें। मुझे उसी कोड बेस में कहीं और थ्रेड सुरक्षित रेफ गिनती की आवश्यकता है। – mpipe3

3

आंद्रेई Alexandrescu CppCon 2014

वीडियो here

और स्लाइड here

मैं वास्तव में लगता है कि देखें में (कुछ अतिरिक्त अनुकूलन के साथ) अपनी खुद की एक लड़ी साझा सूचक वर्ग को लागू करने के बारे में बात मानक या बूस्ट को उनके साझा ptrs में परमाणु रेफ गिनती का उपयोग करने के लिए टेम्पलेट पैरामीटर की आपूर्ति करनी चाहिए हालांकि ...

संबंधित मुद्दे