multithreading - रिकर्सिव लॉक(म्यूटेक्स) बनाम गैर-रिकर्सिव लॉक(म्यूटेक्स)




locking mutex (4)

म्यूटेक्स का उपयोग करने के लिए सही मानसिक मॉडल: म्यूटेक्स एक आविष्कार की रक्षा करता है।

आप निश्चित क्यों हैं कि म्यूटेक्स का उपयोग करने के लिए यह वास्तव में सही मानसिक मॉडल है? मुझे लगता है कि सही मॉडल डेटा की रक्षा कर रहा है लेकिन इनवेरिएंट नहीं।

इनविरिएंट की सुरक्षा की समस्या एकल-थ्रेडेड अनुप्रयोगों में भी प्रस्तुत करती है और इसमें बहु-थ्रेडिंग और म्यूटेक्स के साथ कुछ भी सामान्य नहीं है।

इसके अलावा, अगर आपको इनवेरिएंट की रक्षा करने की ज़रूरत है, तो भी आप बाइनरी सेमफोर का उपयोग कर सकते हैं जो कभी भी रिकर्सिव नहीं होता है।

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

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

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

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


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

हालांकि , यहां भी खेलने पर अन्य विचार हैं।

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

एक अन्य संपत्ति जो म्यूटेक्स में स्वामित्व की भावना के साथ आता है वह प्राथमिकता विरासत का समर्थन करने की क्षमता है। चूंकि कर्नेल म्यूटेक्स के मालिक के धागे को ट्रैक कर सकता है और प्राथमिकता वाले थ्रेड सिस्टम में सभी अवरोधकों की पहचान भी ट्रैक कर सकता है, जो थ्रेड की प्राथमिकता को आगे बढ़ाना संभव हो जाता है जो वर्तमान में म्यूटेक्स का सर्वोच्च प्राथमिकता धागा की प्राथमिकता के लिए है जो वर्तमान में म्यूटेक्स पर अवरुद्ध है। यह विरासत प्राथमिकता विचलन की समस्या को रोकती है जो ऐसे मामलों में हो सकती है। (ध्यान दें कि सभी सिस्टम ऐसे म्यूटेक्स पर प्राथमिकता विरासत का समर्थन नहीं करते हैं, लेकिन यह एक और विशेषता है जो स्वामित्व की धारणा के माध्यम से संभव हो जाती है)।

यदि आप क्लासिक VxWorks RTOS कर्नेल का संदर्भ लेते हैं, तो वे तीन तंत्र परिभाषित करते हैं:

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

फिर, यह मंच द्वारा कुछ हद तक भिन्न होता है - विशेष रूप से वे इन चीजों को क्या कहते हैं, लेकिन यह अवधारणाओं और खेल में विभिन्न तंत्र का प्रतिनिधि होना चाहिए।


जवाब दक्षता नहीं है। गैर-पुनर्वित्त म्यूटेक्स बेहतर कोड का कारण बनता है।

उदाहरण: ए :: foo () लॉक प्राप्त करता है। इसके बाद बी :: बार () कहते हैं। जब आपने इसे लिखा तो यह ठीक काम करता था। लेकिन कुछ समय बाद कोई ए :: बाज़ () को कॉल करने के लिए बी :: बार () को बदलता है, जो लॉक भी प्राप्त करता है।

खैर, अगर आपके पास रिकर्सिव म्यूटेक्स नहीं हैं, तो यह डेडलॉक्स। यदि आपके पास है, तो यह चलता है, लेकिन यह तोड़ सकता है। ए :: foo () ने ऑब्जेक्ट बार () को कॉल करने से पहले एक असंगत स्थिति में छोड़ा हो सकता है, इस धारणा पर कि baz () रन नहीं चला सका क्योंकि यह म्यूटेक्स भी प्राप्त करता है। लेकिन शायद यह नहीं चलना चाहिए! जिस व्यक्ति ने ए :: फू () लिखा था, उसने माना कि कोई भी एक ही समय में ए :: बाज़ () को कॉल नहीं कर सकता - यही कारण है कि दोनों विधियों ने लॉक हासिल किया।

म्यूटेक्स का उपयोग करने के लिए सही मानसिक मॉडल: म्यूटेक्स एक आविष्कार की रक्षा करता है। जब म्यूटेक्स आयोजित होता है, तो परिवर्तक बदल सकता है, लेकिन म्यूटेक्स को रिहा करने से पहले, आविष्कार फिर से स्थापित किया जाता है। पुनर्विक्रेता ताले खतरनाक हैं क्योंकि दूसरी बार जब आप लॉक प्राप्त करते हैं तो आप यह सुनिश्चित नहीं कर सकते कि invariant अब और सत्य है।

यदि आप पुनर्विक्रेता ताले से खुश हैं, तो यह केवल इसलिए है क्योंकि आपको पहले इस तरह की समस्या को डीबग नहीं करना पड़ा है। जावा ने इन दिनों java.util.concurrent.locks में गैर-पुनर्विक्रेता ताले लगाए हैं।


रिकर्सन म्यूटेक्स के लिए एकमात्र अच्छा उपयोग केस तब होता है जब किसी ऑब्जेक्ट में कई विधियां होती हैं। जब किसी भी तरीके ऑब्जेक्ट की सामग्री को संशोधित करता है, और इसलिए स्थिति को फिर से संगत होने से पहले ऑब्जेक्ट को लॉक करना होगा।

यदि विधियां अन्य तरीकों का उपयोग करती हैं (यानी: addNewArray () addNewPoint () को कॉल करती है, और रिकैकबॉउंड () के साथ अंतिम रूप देती है, लेकिन उनमें से किसी भी फ़ंक्शन को म्यूटेक्स को लॉक करने की आवश्यकता होती है, तो रिकर्सिव म्यूटेक्स एक जीत-जीत है।

किसी भी अन्य मामले के लिए (केवल खराब कोडिंग को हल करना, इसे विभिन्न वस्तुओं में भी उपयोग करना) स्पष्ट रूप से गलत है!





recursive-mutex