पुनरावर्ती ताला (Mutex) बनाम गैर-पुनरावर्ती ताला (Mutex)


183

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

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

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

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

जवाबों:


153

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

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

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

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

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

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

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


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

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

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

शायद यह अनुभवहीन है, लेकिन मैं इस धारणा के तहत था कि एक म्यूटेक्स का केंद्रीय विचार यह है कि लॉकिंग थ्रेड म्यूटेक्स को अनलॉक करता है और फिर अन्य थ्रेड्स भी ऐसा कर सकते हैं। कंप्यूटिंग .
llnl.gov/tutorials/pthreads

2
@curiousguy - एक प्रसारण रिलीज़ किसी भी और सभी थ्रेड्स को स्पष्ट रूप से दिए बिना सेमीफ़ायर पर अवरुद्ध कर देता है (खाली रहता है) जबकि एक सामान्य द्विआधारी देना केवल प्रतीक्षा कतार के सिर पर धागा जारी करेगा (यह मानते हुए कि एक अवरुद्ध है)।
लंबा जेफ़

122

उत्तर दक्षता नहीं है। गैर-रीएंन्ट्रेंट म्यूटेक्स बेहतर कोड का नेतृत्व करते हैं।

उदाहरण: A :: foo () लॉक का अधिग्रहण करता है। इसे तब B :: bar () कहते हैं। जब आपने इसे लिखा तो ठीक काम किया। लेकिन कुछ समय बाद किसी ने बी :: बार () को A :: baz () कॉल करने के लिए बदल दिया, जो लॉक को भी प्राप्त करता है।

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

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

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


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

1
@ जोनाथन करता है जावा जावा में इन दिनों गैर-रीवेंटेंट ताले हैं। jut.conil.concurrent.locks ??
user454322

1
+1 मेरा अनुमान है, कि रीएन्ट्रेंट लॉक के लिए सबसे आम उपयोग एक एकल वर्ग के अंदर है, जहाँ कुछ तरीकों को गार्डेड और नॉन-गार्डेड कोड दोनों से बुलाया जा सकता है। यह वास्तव में हमेशा तथ्यहीन हो सकता है। @ user454322 ज़रूर, Semaphore
मैार्टिनस

1
मेरी गलतफहमी को क्षमा करें, लेकिन मैं यह नहीं देखता कि यह म्यूटेक्स के लिए कैसे प्रासंगिक है। मान लीजिए कि कोई मल्टीथ्रेडिंग और लॉकिंग शामिल नहीं है, फिर A::foo()भी कॉल करने से पहले ऑब्जेक्ट को असंगत स्थिति में छोड़ दिया हो सकता है A::bar()। म्यूटेक्स, पुनरावर्ती या नहीं, का इस मामले से कोई लेना-देना नहीं है?
सियुआन रेन

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

92

जैसा कि डेव ब्यूटेनहोफ ने खुद लिखा है :

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


9
ब्यूटेनहोफ़ की प्रतिक्रिया में अंतिम भाग पर भी ध्यान दें: ...you're not DONE until they're [recursive mutex] all gone.. Or sit back and let someone else do the design.
user454322

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

13

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

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

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

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


सच। एक आक्रमणकारी की रक्षा के लिए बेहतर तंत्र हैं।
ActiveTrayPrntrTagDataStrDrvr

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

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

4

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


3

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

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

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


1

गैर-पुनरावर्ती म्यूटेक्स क्या हैं?

वे पूरी तरह से अच्छे हैं जब आपको यह सुनिश्चित करना होगा कि कुछ करने से पहले म्यूटेक्स अनलॉक किया गया है। ऐसा इसलिए है क्योंकि pthread_mutex_unlockम्यूटेक्स को केवल गैर-पुनरावर्ती होने पर अनलॉक किया जा सकता है।

pthread_mutex_t      g_mutex;

void foo()
{
    pthread_mutex_lock(&g_mutex);
    // Do something.
    pthread_mutex_unlock(&g_mutex);

    bar();
}

यदि g_mutexगैर-पुनरावर्ती है, तो ऊपर दिए गए कोड bar()को म्यूटेक्स अनलॉक किए जाने के साथ कॉल करने की गारंटी है

इस प्रकार मामले में गतिरोध की संभावना को समाप्त bar()करने के लिए एक अज्ञात बाहरी कार्य होता है जो अच्छी तरह से कुछ कर सकता है जिसके परिणामस्वरूप एक और धागा हो सकता है जो एक ही म्यूटेक्स प्राप्त करने की कोशिश कर रहा है। थ्रेड पूल पर बनाए गए एप्लिकेशनों और वितरित अनुप्रयोगों में इस तरह के परिदृश्य असामान्य नहीं हैं, जहां एक इंटरप्रोसेस कॉल क्लाइंट प्रोग्रामर के बिना एक नया धागा स्पॉन कर सकता है जो कि साकार भी कर सकता है। ऐसे सभी परिदृश्यों में ताला जारी होने के बाद ही उक्त बाहरी कार्यों को लागू करना सबसे अच्छा है।

यदि g_mutexपुनरावर्ती था, तो कॉल करने से पहले यह सुनिश्चित करने का कोई तरीका नहीं होगा कि इसे अनलॉक किया गया है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.