2008-10-09 17 views
152

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

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

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

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

उत्तर

123

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

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

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

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

आप क्लासिक VxWorks RTOS कर्नेल का उल्लेख है, वे तीन प्रक्रियाओं को परिभाषित: - प्रत्यावर्तन, और वैकल्पिक रूप प्राथमिकता विरासत

  • बाइनरी सेमाफोर का समर्थन करता है -

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

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

  • +4

    गैर पुनरावर्ती म्युटेक्स के बारे में अपने स्पष्टीकरण अधिक एक सेमाफोर तरह लग रहा था। एक म्यूटेक्स (चाहे रिकर्सिव या गैर-रिकर्सिव) स्वामित्व की धारणा है। –

    +0

    @JayD जब लोग इन तरह की चीजों के बारे में बहस करते हैं तो यह बहुत भ्रमित होता है .. तो इन चीजों को परिभाषित करने वाली इकाई कौन है? – Pacerier

    +8

    @Pacerier प्रासंगिक मानक। यह उत्तर उदा। पॉज़िक्स (pthreads) के लिए गलत है, जहां थ्रेड के अलावा थ्रेड में सामान्य म्यूटेक्स को अनलॉक करना है, जो इसे लॉक करता है, अपरिभाषित व्यवहार है, जबकि एक त्रुटि जांच या रिकर्सिव mutex परिणामों के साथ ऐसा करने के लिए एक संभावित त्रुटि कोड में होता है। अन्य सिस्टम और मानक बहुत अलग व्यवहार कर सकते हैं। – nos

    103

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

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

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

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

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

    +4

    यह मुझे कुछ समय लिया तुम्हारे बारे में अपरिवर्तनीय मान्य नहीं किया जा रहा है क्या कह रहे थे जब आप लॉक दूसरी बार हड़पने के लिए। अच्छी बात! क्या होगा यदि यह एक रीड-राइट लॉक (जैसे जावा के रीडवाइट लॉक) थे और आपने रीड लॉक हासिल किया था और फिर उसी थ्रेड में दूसरी बार रीड लॉक को फिर से हासिल किया था। रीड लॉक सही प्राप्त करने के बाद आप एक आविष्कार को अमान्य नहीं करेंगे? तो जब आप दूसरा पठन लॉक प्राप्त करते हैं, तो invariant अभी भी सत्य है। – dgrant

    +1

    @ जोनाथन क्या _ जावा के पास इन दिनों java.util.concurrent.locks_ में गैर-पुनर्विक्रेता ताले हैं ?? – user454322

    +1

    +1 मुझे लगता है कि पुनर्विक्रेता ताला के लिए सबसे आम उपयोग एक वर्ग के अंदर है, जहां कुछ तरीकों को संरक्षित और गैर-संरक्षित कोड टुकड़ों से बुलाया जा सकता है। यह वास्तव में हमेशा बाहर फैक्टर किया जा सकता है। @ user454322 निश्चित रूप से, 'सेमफोर'। – maaartinus

    10

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

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

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

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

    +0

    सच है। एक invariant की रक्षा के लिए बेहतर तंत्र हैं। – ActiveTrayPrntrTagDataStrDrvr

    +6

    यह उस उत्तर की एक टिप्पणी होनी चाहिए जिसने बयान दिया था। म्यूटेक्स न केवल डेटा की रक्षा करते हैं, वे इनवेरिएंट की भी रक्षा करते हैं। म्यूटेक्स के बजाए परमाणुओं (जहां डेटा स्वयं की रक्षा करता है) के मामले में कुछ सरल कंटेनर (सबसे सरल ढेर) लिखने का प्रयास करें और आप कथन को समझेंगे। –

    +0

    म्यूटेक्स डेटा की रक्षा नहीं करते हैं, वे एक आविष्कार की रक्षा करते हैं। हालांकि उस आविष्कार का उपयोग डेटा की सुरक्षा के लिए किया जा सकता है। –

    78

    As written by Dave Butenhof himself:

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

    +8

    इसके अलावा Butenhof की प्रतिक्रिया में अंतिम भाग पर ध्यान दें: '... आप कर नहीं कर रहे हैं, जब तक वे कर रहे हैं [पुनरावर्ती म्युटेक्स] सभी चला गया .. या वापस बैठते हैं और किसी और design.' – user454322

    +1

    उन्होंने यह भी करते हैं बताता है कि एक वैश्विक रिकर्सिव म्यूटेक्स का उपयोग करके (उनकी राय यह है कि आपको केवल एक की आवश्यकता है) एक क्रैच के रूप में ठीक है जब आप बहुभाषी कोड में इसका उपयोग शुरू करते हैं तो बाह्य पुस्तकालय के आविष्कारों को समझने के कड़ी मेहनत को स्थगित कर देते हैं। लेकिन आपको हमेशा के लिए क्रश का उपयोग नहीं करना चाहिए, लेकिन आखिर में कोड के समवर्ती इनवेरिएंट को समझने और ठीक करने के लिए समय का निवेश करना चाहिए। तो हम समझ सकते हैं कि रिकर्सिव म्यूटेक्स का उपयोग तकनीकी ऋण है। – FooF

    2

    एक मुख्य कारण यह है कि पुनरावर्ती mutexes उपयोगी होते हैं एक ही धागे से तरीकों को कई बार तक पहुँचने के मामले में है। उदाहरण के लिए, यदि म्युटेक्स ताला के लिए एक बैंक ए/सी सुरक्षा कर रहा है कहते हैं वापस ले लें, फिर यदि उस वापसी के साथ कोई शुल्क भी जुड़ा हुआ है, तो उसी म्यूटेक्स का उपयोग किया जाना चाहिए।

    0

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

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

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

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