2012-11-20 5 views
7

मैं डेटा के एक टुकड़े को गठबंधन करने के तरीकों को देख रहा हूं जिसे थ्रेड-सुरक्षा के लिए प्रावधान किए गए लॉक के साथ कई थ्रेडों द्वारा एक्सेस किया जाएगा। मुझे लगता है कि मुझे एक ऐसे बिंदु पर जाना है जहां मुझे लगता है कि ऐसा करने के लिए यह संभव नहीं है कि यह स्थिरता बनाए रखे।डेटा और उसके लॉक को जोड़ते समय कॉन्स्ट-सही होना असंभव है?

उदाहरण के लिए निम्नलिखित वर्ग लें:

template <typename TType, typename TMutex> 
class basic_lockable_type 
{ 

public: 
    typedef TMutex lock_type; 

public: 
    template <typename... TArgs> 
    explicit basic_lockable_type(TArgs&&... args) 
     : TType(std::forward<TArgs...>(args)...) {} 

    TType& data() { return data_; } 
    const TType& data() const { return data_; } 

    void lock() { mutex_.lock(); } 
    void unlock() { mutex_.unlock(); } 

private: 
    TType   data_; 
    mutable TMutex mutex_; 

}; 

typedef basic_lockable_type<std::vector<int>, std::mutex> vector_with_lock; 

इस में मैं, डेटा और ताला गठबंधन करने के लिए mutex_mutable के रूप में चिह्नित की कोशिश करो। दुर्भाग्यवश यह पर्याप्त नहीं है क्योंकि मैं इसे देखता हूं क्योंकि vector_with_lock को mutable के रूप में चिह्नित किया जाना चाहिए ताकि const फ़ंक्शन से एक पठन ऑपरेशन किया जा सके जो पूरी तरह से सही नहीं है (data_mutable एक कॉन्स से होना चाहिए) ।

void print_values() const 
{ 
    std::lock_guard<vector_with_lock> lock(values_); 
    for(const int val : values_) 
    { 
     std::cout << val << std::endl; 
    } 
} 

vector_with_lock values_; 

किसी को भी इस तरह के आसपास वैसे भी देख सकते हैं कि स्थिरांक-शुद्धता डेटा संयोजन और ताला जबकि बनाए रखा है? साथ ही, क्या मैंने यहां कोई गलत धारणाएं की हैं?

+2

'लॉक' और 'अनलॉक' कॉन्स बनाएं? (साथ ही, यह 'std :: lock_guard ' नहीं होना चाहिए? यदि आप इसका उपयोग नहीं कर रहे हैं तो आप एक नया लॉक करने योग्य क्यों बनाते हैं?) –

+0

अपडेट किया गया 'lock_guard' – Graeme

+0

@ आर। मार्टिनिन्हो फर्नांडीस बेशक, इन्हें चिह्नित करना कॉन्स एक गैर-परिवर्तनीय 'vector_with_lock' इंस्टेंस कॉल 'लॉक' और' अनलॉक 'की अनुमति देगा, हां? – Graeme

उत्तर

6

व्यक्तिगत रूप से, मैं एक ऐसा डिज़ाइन पसंद करूंगा जहां आपको मैन्युअल रूप से लॉक नहीं करना पड़ेगा, और डेटा इस तरह से ठीक से encapsulated है कि आप वास्तव में पहले लॉक किए बिना इसे एक्सेस नहीं कर सकते हैं।

एक विकल्प यह है कि एक दोस्त को apply या लॉकिंग करने वाला कुछ काम करने के लिए, encapsulated डेटा पकड़ लेता है और इसे उस फ़ंक्शन ऑब्जेक्ट में भेजता है जो उसके अंदर रखे लॉक के साथ चलाया जाता है।

//! Applies a function to the contents of a locker_box 
/*! Returns the function's result, if any */ 
template <typename Fun, typename T, typename BasicLockable> 
ResultOf<Fun(T&)> apply(Fun&& fun, locker_box<T, BasicLockable>& box) { 
    std::lock_guard<BasicLockable> lock(box.lock); 
    return std::forward<Fun>(fun)(box.data); 
} 
//! Applies a function to the contents of a locker_box 
/*! Returns the function's result, if any */ 
template <typename Fun, typename T, typename BasicLockable> 
ResultOf<Fun(T const&)> apply(Fun&& fun, locker_box<T, BasicLockable> const& box) { 
    std::lock_guard<BasicLockable> lock(box.lock); 
    return std::forward<Fun>(fun)(box.data); 
} 

प्रयोग तो हो जाता है:

void print_values() const 
{ 
    apply([](std::vector<int> const& the_vector) { 
     for(const int val : the_vector) { 
      std::cout << val << std::endl; 
     } 
    }, values_); 
} 

वैकल्पिक रूप से, आप सीमा के आधार पर ठीक से गुंजाइश ताला पाश के लिए करने के लिए दुरुपयोग और एक "एकल" आपरेशन के रूप में मान निकाल सकते हैं।

for(auto&& the_vector : box.open()) { 
    // lock is held in this scope 
    // do our stuff normally 
    for(const int val : the_vector) { 
     std::cout << val << std::endl; 
    } 
} 

मुझे लगता है कि एक विवरण क्रम में है: सभी की आवश्यकता होती iterators की उचित सेट है। सामान्य विचार यह है कि open() एक आरएआईआई हैंडल देता है जो निर्माण पर ताला प्राप्त करता है और इसे विनाश पर छोड़ देता है। लूप के लिए रेंज-आधारित इस अस्थायी जीवन को तब तक सुनिश्चित करेगा जब तक कि लूप निष्पादित हो। यह उचित लॉक स्कोप देता है।

कि आरएआईआई हैंडल भी begin() और end() इटरेटर को एकल निहित मान के साथ प्रदान करता है। इस प्रकार हम सुरक्षित डेटा पर जा सकते हैं। रेंज-आधारित लूप हमारे लिए डीरफ्रेंसिंग करने और लूप वैरिएबल को बाध्य करने का ख्याल रखता है। चूंकि रेंज एक सिंगलटन है, इसलिए "लूप" वास्तव में हमेशा एक बार चलती है।

box डेटा पर जाने का कोई अन्य तरीका नहीं प्रदान करना चाहिए, ताकि यह वास्तव में अंतःस्थापित पहुंच को लागू कर सके।

बेशक बॉक्स बंद होने के बाद संदर्भ डेटा के संदर्भ में एक संदर्भ को दूर कर सकता है, इस तरह से बॉक्स बंद होने के बाद संदर्भ उपलब्ध है। लेकिन यह मर्फी के खिलाफ सुरक्षा के लिए है, मच्छियाली नहीं।

निर्माण अजीब लग रहा है, इसलिए मैं इसे किसी भी व्यक्ति को दोष नहीं दूंगा। एक तरफ मैं इसका उपयोग करना चाहता हूं क्योंकि अर्थशास्त्र परिपूर्ण हैं, लेकिन दूसरी तरफ मैं नहीं चाहता क्योंकि यह नहीं है कि यह किस श्रेणी के लिए है। पकड़ने वाले हाथ पर यह रेंज-आरएआईआई हाइब्रिड तकनीक अपेक्षाकृत सामान्य है और इसे अन्य सिरों के लिए आसानी से दुरुपयोग किया जा सकता है, लेकिन मैं इसे आपकी कल्पना/दुःस्वप्न में छोड़ दूंगा;) अपने विवेकाधिकार पर प्रयोग करें।


पाठक के लिए एक व्यायाम है, लेकिन iterators के एक सेट की एक छोटी सी उदाहरण के रूप में छोड़ दिया मेरे अपने locker_box implementation में पाया जा सकता।

+0

ओ_ओ कि 'के लिए' बल्कि अपमानजनक है। – Mankarse

+0

और मैंने अभी अपने कार्यान्वयन में एक बग देखा: एस ओउप्स। घर, बच्चों पर उस कोड का प्रयोग न करें। –

+0

क्या यह अभी भी छोटी है? यदि हां, तो किस कारण से? – sehe

3

आप "कॉन्स्ट सही" से क्या समझते हैं? आम तौर पर, मुझे लगता है कि तार्किक आधार के लिए सर्वसम्मति है, जिसका अर्थ है कि यदि म्यूटेक्स आपके ऑब्जेक्ट की तार्किक (या देखने योग्य) स्थिति का हिस्सा नहीं है, तो mutable घोषित करने में कुछ भी गलत नहीं है, और इसे यहां तक ​​कि कॉन्स्ट फ़ंक्शंस में भी उपयोग करना गलत है ।

0

एक अर्थ में, चाहे म्यूटेक्स लॉक हो या ऑब्जेक्ट की अवलोकन योग्य स्थिति का हिस्सा न हो - आप इसे उदाहरण के लिए गलती से लॉकिंग इनवर्जन बनाकर देख सकते हैं।

यह स्व-लॉकिंग ऑब्जेक्ट्स के साथ एक मौलिक मुद्दा है, और मुझे लगता है कि इसका एक पहलू कॉन्स्ट-शुद्धता से संबंधित है।

या तो आप किसी संदर्भ-टू-कॉन्स के माध्यम से ऑब्जेक्ट की "लॉकनेस" बदल सकते हैं, अन्यथा आप संदर्भ-से-कॉन्स्ट के माध्यम से सिंक्रनाइज़ एक्सेस नहीं कर सकते हैं। एक, संभवतः पहले उठाओ।

विकल्प यह सुनिश्चित करना है कि लॉक किए गए राज्य में ऑब्जेक्ट को कॉलिंग कोड द्वारा "मनाया" नहीं जा सके, ताकि लॉकनेस अवलोकन योग्य राज्य का हिस्सा न हो। लेकिन फिर एक कॉलर के लिए एक ही सिंक्रनाइज़ ऑपरेशन के रूप में उनके vector_with_lock में प्रत्येक तत्व पर जाने का कोई तरीका नहीं है। जैसे ही आप लॉक के साथ उपयोगकर्ता के कोड को कॉल करते हैं, वे एक संभावित या गारंटीकृत लॉकिंग इनवर्जन युक्त कोड लिख सकते हैं, जो "देखता है" कि लॉक हो या नहीं। इसलिए संग्रह के लिए यह समस्या का समाधान नहीं करता है।

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