2016-01-28 10 views
8

std::shared_ptrhas specializations for atomic operationsatomic_compare_exchange_weak और परिवार की तरह परमाणु संचालन, लेकिन std::unique_ptr के लिए समकक्ष विशेषज्ञता पर दस्तावेज़ीकरण नहीं मिल रहा है। क्या वहां पर कोई? यदि नहीं, तो क्यों नहीं?`unique_ptr`

उत्तर

6

std::unique_ptr के लिए कोई मानक परमाणु कार्य नहीं है।

मैं के लिए एक तर्क मिला क्यों Atomic Smart Pointers(N4058) में नहीं हर्ब Sutter द्वारा

लॉरेंस Crowl जोड़ने के लिए जवाब दिया:

कारण है कि ताला shared_ptr का एक तरीका यह भी है है है एक स्थिति से बचने के लिए जिसमें हम परमाणु टेम्पलेट पैरामीटर पर पूर्व शर्त को कमजोर करते हैं कि यह मामूली है, और इसलिए डेडलॉक का कोई खतरा नहीं है।

उस ने कहा, हम आवश्यकता को कमजोर कर सकते हैं ताकि तर्क प्रकार को केवल लॉकफ्री, या शायद केवल गैर-पुनरावर्ती लॉकिंग की आवश्यकता हो।

हालांकि, जबकि मामूली परीक्षण योग्य गुणों के लिए तुच्छ बनाता है, मुझे कमजोर संपत्ति के परीक्षण के लिए कोई प्रभावी तंत्र नहीं दिखता है।

यह प्रस्ताव Concurrency Subgroup को सौंपा गया है और अभी तक कोई स्वभाव नहीं है। आप JTC1/SC22/WG21 - Papers 2014 मेलिंग2014-07

+2

यह संदर्भ कारण बताता है कि क्यों (प्री सी ++ 17) 'std :: atomic स्मार्ट पॉइंटर्स के साथ काम नहीं करता है, लेकिन क्यों नहीं ओवरलोड नहीं हैं उदा। std :: unique_ptr के लिए 'atomic_compare_exchange_weak', जबकि std :: shared_ptr – MikeMB

+1

के लिए हैं, औसत समय में, N4058 को N4162 के रूप में संशोधित किया गया है। आप इसे मेलिंग2014-10 में उपरोक्त लिंक पर पा सकते हैं। – Jan

4

पर ध्यान दें, सावधान रहें, धागे के बीच एक संशोधित unique_ptr साझा करना शायद ही कभी समझ में आता है, भले ही पॉइंटर स्वयं परमाणु था। यदि इसकी सामग्री बदलती है, तो अन्य धागे इसके बारे में कैसे जान सकते हैं? वे नहीं कर सकते

unique_ptr<MyObject> p(new MyObject); 

// Thread A 
auto ptr = p.get(); 
if (ptr) { 
    ptr->do_something(); 
} 

// Thread B 
p.reset(); 

कैसे p.get() बुला के बाद एक dangling सूचक का उपयोग कर एक से बचने के थ्रेड कर सकते हैं:

इस उदाहरण पर विचार?

यदि आप थ्रेड के बीच कोई ऑब्जेक्ट साझा करना चाहते हैं, तो shared_ptr का उपयोग करें जिसमें इस उद्देश्य के लिए बिल्कुल संदर्भ गणना है।

#pragma once 
#include <atomic> 
#include <memory> 

template<class T> 
class atomic_unique_ptr 
{ 
    using pointer = T *; 
    std::atomic<pointer> ptr; 
public: 
    constexpr atomic_unique_ptr() noexcept : ptr() {} 
    explicit atomic_unique_ptr(pointer p) noexcept : ptr(p) {} 
    atomic_unique_ptr(atomic_unique_ptr&& p) noexcept : ptr(p.release()) {} 
    atomic_unique_ptr& operator=(atomic_unique_ptr&& p) noexcept { reset(p.release()); return *this; } 
    atomic_unique_ptr(std::unique_ptr<T>&& p) noexcept : ptr(p.release()) {} 
    atomic_unique_ptr& operator=(std::unique_ptr<T>&& p) noexcept { reset(p.release()); return *this; } 

    void reset(pointer p = pointer()) { auto old = ptr.exchange(p); if (old) delete old; } 
    operator pointer() const { return ptr; } 
    pointer operator->() const { return ptr; } 
    pointer get() const { return ptr; } 
    explicit operator bool() const { return ptr != pointer(); } 
    pointer release() { return ptr.exchange(pointer()); } 
    ~atomic_unique_ptr() { reset(); } 
}; 

template<class T> 
class atomic_unique_ptr<T[]> // for array types 
{ 
    using pointer = T *; 
    std::atomic<pointer> ptr; 
public: 
    constexpr atomic_unique_ptr() noexcept : ptr() {} 
    explicit atomic_unique_ptr(pointer p) noexcept : ptr(p) {} 
    atomic_unique_ptr(atomic_unique_ptr&& p) noexcept : ptr(p.release()) {} 
    atomic_unique_ptr& operator=(atomic_unique_ptr&& p) noexcept { reset(p.release()); return *this; } 
    atomic_unique_ptr(std::unique_ptr<T>&& p) noexcept : ptr(p.release()) {} 
    atomic_unique_ptr& operator=(std::unique_ptr<T>&& p) noexcept { reset(p.release()); return *this; } 

    void reset(pointer p = pointer()) { auto old = ptr.exchange(p); if (old) delete[] old; } 
    operator pointer() const { return ptr; } 
    pointer operator->() const { return ptr; } 
    pointer get() const { return ptr; } 
    explicit operator bool() const { return ptr != pointer(); } 
    pointer release() { return ptr.exchange(pointer()); } 
    ~atomic_unique_ptr() { reset(); } 
}; 

एनबी:: इस पोस्ट में प्रदान किए गए कोड एतद्द्वारा में मिल जाता है


तुम सच में यह चाहते थे , तो आप हमेशा अपनी खुद की atomic_unique_ptr, पंक्तियों के साथ कुछ (सरलीकृत) रोल कर सकते हैं पब्लिक डोमेन।

+6

यह वास्तव में पूरी तरह से समझ में आता है: यह वास्तव में ढेर पर ऑब्जेक्ट बनाने के लिए एक सामान्य लॉकफ्री पैटर्न है - और एक बार जब आप इसके साथ काम कर लेंगे - एक साझा चर में एक पॉइंटर संग्रहीत करना। अब, यदि कोई थ्रेड ऑब्जेक्ट तक पहुंचना चाहता है, तो पहले इसे उस पॉइंटर को नलप्टर के साथ एक्सचेंज करना होगा और थ्रेड समाप्त होने के बाद पॉइंटर को वापस स्टोर करना होगा। इस तरह आप सुनिश्चित करते हैं कि एक ही समय में केवल एक थ्रेड के पास किसी ऑब्जेक्ट तक पहुंच हो। – MikeMB

+0

आप अपने जीवन को इस तरह जटिल क्यों करते हैं ?? बाहरी ऑब्जेक्ट को जीवन चक्र प्रबंधन छोड़ दें जो सभी कार्यकर्ता धागे से बाहर निकलता है और थ्रेड के बीच साझा करने के लिए * अन्य *, परमाणु कच्चे सूचक का उपयोग करता है। – rustyx

+0

E.g.because एक निर्माता है और एक उपभोक्ता है (या आपके पास कई उपभोक्ता भी हैं)। फिर आप जो भी पॉइंटर प्राप्त करते हैं, स्वामित्व स्थानांतरित करना चाहते हैं। किसी भी मामले में: मान लीजिए कि आपके पास परमाणु अद्वितीय_प्टर है, तो मुझे नहीं लगता कि यह चीजों को सरल बनाता है जब आप दृश्यता/अभिगम्यता से अलग-अलग जीवनकाल संभालते हैं। – MikeMB

4

कारण यह है कि std::shared_ptr का परमाणु उदाहरण प्रदान करना संभव है और std::unique_ptr के लिए ऐसा करना संभव नहीं है, उनके हस्ताक्षर में संकेत दिया गया है। की तुलना करें:

  • std::shared_ptr<T> बनाम
  • std::unique_ptr<T, D> जहां D Deleter का प्रकार है।एक नियंत्रण ब्लॉक जहां मजबूत और कमजोर गिनती रखा जाता है आवंटित करने के लिए

std::shared_ptr जरूरत है, तो टाइप-विलोपन Deleter की एक छोटी सी कीमत पर आया था (एक बस थोड़ा बड़ा नियंत्रण ब्लॉक)।

नतीजतन, std::shared_ptr<T> के लेआउट आम तौर पर के समान है:

template <typename T> 
struct shared_ptr { 
    T* _M_ptr; 
    SomeCounterClass<T>* _M_counters; 
}; 

और यह atomically उन दो संकेत के आदान-प्रदान को करने के लिए संभव है।


std::unique_ptr में शून्य-ओवरहेड नीति है; एक कच्चे सूचक का उपयोग करने की तुलना में std::unique_ptr का उपयोग किसी भी ओवरहेड को नहीं लेना चाहिए। ताकि जब भी डी शून्य आकार तो sizeof(unique_ptr<T>) == sizeof(T*) है

template <typename T, typename D = default_delete<T>> 
struct unique_ptr { 
    tuple<T*, D> _M_t; 
}; 

कहाँ tuple EBO (खाली बेस अनुकूलन) का उपयोग करता है:

नतीजतन, std::unique_ptr<T, D> के लेआउट आम तौर पर के समान है।

हालांकि, ऐसे मामलों में जहां D शून्य आकार नहीं है, कार्यान्वयन नीचे करने के लिए फोड़े:

template <typename T, typename D = default_delete<T>> 
struct unique_ptr { 
    T* _M_ptr; 
    D _M_del; 
}; 

यह D किकर यहाँ है; सामान्य रूप से यह सुनिश्चित करना संभव नहीं है कि D को म्यूटेक्स पर भरोसा किए बिना परमाणु फैशन में आदान-प्रदान किया जा सके।

इसलिए, std::atomic_compare_exchange* जेनेरिक std::unique_ptr<T, D> के लिए विशेष दिनचर्या का सूट प्रदान करना संभव नहीं है।

ध्यान दें कि मानक यह भी गारंटी नहीं देता है कि sizeof(unique_ptr<T>) == sizeof(T*) AFAIK, हालांकि यह एक सामान्य अनुकूलन है।