दो अलग-अलग चिंताओं (थ्रेड-सुरक्षा और अपवाद-सुरक्षा) हैं और उन्हें अलग से संबोधित करना सबसे अच्छा लगता है। सदस्यों को प्रारंभ करते समय लॉक प्राप्त करने के लिए कन्स्ट्रक्टर को किसी अन्य ऑब्जेक्ट को तर्क के रूप में लेने की अनुमति देने के लिए, डेटा सदस्यों को किसी भी अलग वर्ग में कारक बनाना आवश्यक है: इस प्रकार सबबोजेक्ट प्रारंभ होने पर क्लास अधिग्रहित किया जा सकता है और वास्तविक डेटा को बनाए रखने वाला वर्ग किसी भी सहमति मुद्दों को नजरअंदाज कर सकते हैं। इस प्रकार, कक्षा को दो भागों में विभाजित किया जाएगा: class A
डेटा को बनाए रखने के लिए समवर्ती मुद्दों और class A_unlocked
से निपटने के लिए। चूंकि A_unlocked
के सदस्य फ़ंक्शंस में कोई समवर्ती सुरक्षा नहीं है, इसलिए उन्हें इंटरफ़ेस में सीधे प्रकट नहीं किया जाना चाहिए और इस प्रकार, A_unlocked
को A
का निजी सदस्य बनाया गया है।
एक अपवाद-सुरक्षित असाइनमेंट ऑपरेटर बनाना सीधे कॉपी कन्स्ट्रक्टर का लाभ उठाने वाला है। तर्क की नकल की है और सदस्यों को लगा दिया जाता था:
A_unlocked& A_unlocked::operator= (A_unlocked const& other) {
A_unlocked(other).swap(*this);
return *this;
}
बेशक
, इसका मतलब है कि एक उपयुक्त प्रतिलिपि निर्माता और एक swap()
सदस्य लागू किया जाता है। कई संसाधनों के आवंटन से निपटने, उदाहरण के लिए, ढेर पर आवंटित कई ऑब्जेक्ट्स, प्रत्येक ऑब्जेक्ट के लिए उपयुक्त संसाधन हैंडलर द्वारा किया जा सकता है। संसाधन हैंडलर के उपयोग के बिना अपवाद फेंकने के मामले में सभी संसाधनों को सही ढंग से साफ करने के लिए यह बहुत गन्दा हो जाता है। ढेर आवंटित स्मृति को बनाए रखने के उद्देश्य से std::unique_ptr<T>
(या std::auto_ptr<T>
यदि आप सी ++ 2011 का उपयोग नहीं कर सकते हैं) एक उपयुक्त विकल्प है। नीचे दिया गया कोड केवल वस्तुओं की ओर इशारा करता है हालांकि उन्हें सदस्यों को बनाने के बजाय ढेर पर वस्तुओं को आवंटित करने में बहुत अधिक बिंदु नहीं है।
class A_unlocked {
private:
std::unique_ptr<B> pb;
std::unique_ptr<C> pc;
// ...
public:
A_unlocked(/*...*/);
A_unlocked(A_unlocked const& other);
A_unlocked& operator= (A_unlocked const& other);
void swap(A_unlocked& other);
// ...
};
A_unlocked::A_unlocked(A_unlocked const& other)
: pb(new B(*other.pb))
, pc(new C(*other.pc))
{
}
void A_unlocked::swap(A_unlocked& other) {
using std::swap;
swap(this->pb, other.pb);
swap(this->pc, other.pc);
}
धागे की सुरक्षा बिट यह आवश्यक है पता चला है कि कोई अन्य धागे से खिलवाड़ कर रहा है के लिए: एक वास्तविक उदाहरण में वस्तुओं शायद सही प्रकार का ऑब्जेक्ट बनाने के लिए एक clone()
विधि या किसी अन्य तरीके लागू करेगा कॉपी ऑब्जेक्ट। ऐसा करने का तरीका एक म्यूटेक्स का उपयोग कर रहा है। ,
class A {
private:
mutable std::mutex d_mutex;
A_unlocked d_data;
public:
A(/*...*/);
A(A const& other);
A& operator= (A const& other);
// ...
};
ध्यान दें कि A
के सभी सदस्यों को कुछ संगामिति संरक्षण करने की आवश्यकता होगी प्रकार A
की वस्तुओं बाहरी लॉकिंग के बिना इस्तेमाल किया जा के लिए होती हैं, तो: जो है, class A
कुछ इस तरह लग रहा है।चूंकि म्यूटेक्स समवर्ती पहुंच के खिलाफ सुरक्षा के लिए उपयोग किया जाता है, वास्तव में वस्तु के राज्य का हिस्सा नहीं है, लेकिन ऑब्जेक्ट के राज्य को पढ़ने के दौरान भी बदला जाना चाहिए, इसे mutable
बनाया गया है। जगह में इस के साथ, एक प्रति निर्माता बनाने सीधे आगे है:
A::A(A const& other)
: d_data((std::unique_lock<std::mutex>(other.d_mutex), other.d_data)) {
}
इस सदस्य के प्रति निर्माता के लिए तर्क के म्युटेक्स और प्रतिनिधियों अवरोधित करता है। लॉक स्वचालित रूप से अभिव्यक्ति के अंत में जारी किया जाता है, यह स्वतंत्र है कि प्रतिलिपि सफल थी या अपवाद फेंक दिया गया था। निर्मित वस्तु को किसी लॉकिंग की आवश्यकता नहीं है क्योंकि इस तरह के ऑब्जेक्ट के बारे में कोई अन्य धागा नहीं जानता है।
असाइनमेंट ऑपरेटर का मूल तर्क भी अपने असाइनमेंट ऑपरेटर का उपयोग करके आधार पर प्रतिनिधि करता है। मुश्किल बात यह है कि दो म्यूटेक्स हैं जिन्हें लॉक करने की आवश्यकता है: ऑब्जेक्ट के लिए एक और तर्क के लिए एक। चूंकि एक और थ्रेड दो वस्तुओं को विपरीत तरीके से असाइन कर सकता है, इसलिए मृत-लॉक की संभावना है। सुविधाजनक रूप से, मानक सी ++ लाइब्रेरी std::lock()
एल्गोरिदम प्रदान करती है जो ताले को उपयुक्त तरीके से प्राप्त करती है जो मृत-ताले से बचाती है। एक तरीका यह एल्गोरिथ्म का उपयोग करने के खुला std::unique_lock<std::mutex>
वस्तुओं में पारित करने के लिए है, आवश्यकता होने पर प्रत्येक म्युटेक्स के लिए एक का अधिग्रहण किया जाना है:
A& A::operator= (A const& other) {
if (this != &other) {
std::unique_lock<std::mutex> guard_this(this->d_mutex, std::defer_lock);
std::unique_lock<std::mutex> guard_other(other.d_mutex, std::defer_lock);
std::lock(guard_this, guard_other);
*this->d_data = other.d_data;
}
return *this;
}
एक अपवाद फेंक दिया जाता है काम के दौरान किसी भी बिंदु पर, तो लॉक गार्ड mutexes जारी करेंगे और संसाधन हैंडलर किसी भी नए आवंटित संसाधन को जारी करेंगे। इस प्रकार, उपर्युक्त दृष्टिकोण मजबूत अपवाद गारंटी लागू करता है। दिलचस्प बात यह है कि कॉपी असाइनमेंट को एक ही म्यूटेक्स को दो बार लॉक करने से रोकने के लिए एक स्व-असाइनमेंट जांच करने की आवश्यकता होती है। आम तौर पर, मैं यह मानता हूं कि एक आवश्यक स्व-असाइनमेंट जांच एक संकेत है कि असाइनमेंट ऑपरेटर अपवाद सुरक्षित नहीं है लेकिन मुझे लगता है कि उपर्युक्त कोड अपवाद सुरक्षित है।
यह उत्तर का एक प्रमुख पुनर्लेख है। इस उत्तर के पहले संस्करण या तो खोए गए अपडेट या मृत-लॉक के लिए प्रवण थे। समस्याओं को इंगित करने के लिए याक के लिए धन्यवाद। हालांकि मुद्दों को हल करने के नतीजे में अधिक कोड शामिल है, मुझे लगता है कि कोड का प्रत्येक व्यक्तिगत भाग वास्तव में सरल है और इसकी शुद्धता के लिए जांच की जा सकती है।
एक धागा सुरक्षित असाइनमेंट ऑपरेटर (सुनिश्चित करें कि आपके ताले रहे आरए II, एक एसटीडी आरए II सूचक धारक में सभी आबंटित स्मृति (यदि आप के बाद उस पर सौंपने जारी करने के लिए) की क्षमता के साथ की दुकान, बनाओ आदि)? उन साक्षात्कारकर्ता वास्तव में इसे – stijn
अपवाद को किस डिग्री से सुरक्षित कर रहे हैं? –
सबकुछ-सुरक्षित-कुछ भी कैसे कार्यान्वित करें? निश्चित रूप से सादे सी पॉइंटर्स का उपयोग करके और ऑब्जेक्ट्स की प्रतियों के साथ गड़बड़ नहीं करते हैं ... वे सुरक्षा के लिए आरएआईआई और स्मार्ट पॉइंटर्स हैं, है ना? – leftaroundabout