म्यूटेक्स और क्रिटिकल सेक्शन में क्या अंतर है?


134

कृपया लिनक्स, विंडोज के दृष्टिकोण से समझाएं?

मैं C # में प्रोग्रामिंग कर रहा हूं, क्या इन दोनों शब्दों से फर्क पड़ेगा। कृपया उदाहरणों और जैसे…

धन्यवाद

जवाबों:


232

विंडोज के लिए, महत्वपूर्ण खंड म्यूटेक्स की तुलना में हल्के वजन वाले हैं।

म्यूटेक्स को प्रक्रियाओं के बीच साझा किया जा सकता है, लेकिन हमेशा कर्नेल में एक सिस्टम कॉल का परिणाम होता है जिसमें कुछ ओवरहेड होता है।

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

मैंने एक त्वरित नमूना ऐप लिखा है जो उन दोनों के बीच के समय की तुलना करता है। 1,000,000 अप्रतिबंधित परिचितों और रिलीज़ के लिए मेरे सिस्टम पर, एक म्यूटेक्स एक सेकंड से अधिक लेता है। एक महत्वपूर्ण खंड 1,000,000 प्राप्तियों के लिए ~ 50 एमएस लेता है।

यहां परीक्षण कोड है, मैंने इसे चलाया और यदि म्यूटेक्स पहले या दूसरे स्थान पर है, तो इसी तरह के परिणाम मिले हैं, इसलिए हम कोई अन्य प्रभाव नहीं देख रहे हैं।

HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);

LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;

// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    EnterCriticalSection(&critSec);
    LeaveCriticalSection(&critSec);
}

QueryPerformanceCounter(&end);

int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);

QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    WaitForSingleObject(mutex, INFINITE);
    ReleaseMutex(mutex);
}

QueryPerformanceCounter(&end);

int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

printf("Mutex: %d CritSec: %d\n", totalTime, totalTimeCS);

1
यह सुनिश्चित नहीं है कि यह संबंधित है या नहीं (क्योंकि मैंने संकलन नहीं किया है और आपके कोड की कोशिश नहीं की है), लेकिन मैंने पाया है कि INFINITE के साथ WaitForSingleObject को कॉल करना खराब प्रदर्शन का परिणाम है। 1 के टाइमआउट मान को पास करना, फिर लौटते समय जाँच करना कि मेरे कुछ कोड के प्रदर्शन में भारी अंतर आया है। यह ज्यादातर बाहरी प्रक्रिया के हैंडल के इंतजार के संदर्भ में है, हालांकि ... म्यूटेक्स नहीं। YMMV। मुझे यह देखने में दिलचस्पी होगी कि कैसे म्यूटेक्स उस संशोधन के साथ प्रदर्शन करता है। इस परीक्षण से परिणामी समय अंतर अपेक्षा से बड़ा लगता है।
ट्रॉय हॉवर्ड

5
@ ट्रॉयहौर्ड आप मूल रूप से उस बिंदु पर केवल स्पिन लॉकिंग नहीं कर रहे हैं?
dss539

इस भेद के कारण मुख्यतः ऐतिहासिक हैं। लॉकिंग को लागू करना मुश्किल नहीं है, जो कि अनियंत्रित मामले में क्रिटिकलसेक्शन के रूप में तेज़ है (कुछ परमाणु निर्देश, कोई syscalls), फिर भी प्रक्रियाओं के दौरान काम करता है (साझा स्मृति के एक टुकड़े के साथ)। उदाहरण के लिए लिनक्स futexes देखें ।
रेगनेर

2
@TroyHoward अपने CPU को हर समय 100% चलाने के लिए मजबूर करने की कोशिश करता है और देखता है कि INFINITE बेहतर काम करता है या नहीं। बिजली की रणनीति मेरी मशीन (डेल एक्सपीएस -8700) पर 40 सेंटीमीटर तक ले जा सकती है, ताकि पूरी तरह से धीमा होने का फैसला करने के बाद पूरी गति तक वापस क्रॉल किया जा सके, जो कि अगर आप सोते हैं या केवल एक मिली सेकंड के लिए इंतजार नहीं करते हैं।
स्टीवंस मिलर

मुझे यकीन नहीं है कि मैं समझता हूं कि यहां क्या प्रदर्शन किया जा रहा है। आम तौर पर, एक महत्वपूर्ण खंड में प्रवेश करने के लिए किसी प्रकार के सेमाफोर की आवश्यकता होती है। क्या आप कह रहे हैं कि पर्दे के पीछे, ओ / एस के पास म्यूटेक्स की आवश्यकता के बिना इस महत्वपूर्ण अनुभाग व्यवहार को लागू करने का एक कुशल तरीका है?
एसएन

89

सैद्धांतिक दृष्टिकोण से, एक महत्वपूर्ण खंड कोड का एक टुकड़ा है जिसे एक साथ कई थ्रेड्स द्वारा नहीं चलाया जाना चाहिए क्योंकि कोड साझा संसाधनों तक पहुंचता है।

एक म्यूटेक्स एक एल्गोरिथ्म है (और कभी-कभी एक डेटा संरचना का नाम) जिसका उपयोग महत्वपूर्ण वर्गों की सुरक्षा के लिए किया जाता है।

सेमीफोर और मॉनिटर एक म्यूटेक्स के सामान्य कार्यान्वयन हैं।

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

लाभकारी तुल्यकालन आदिम।

संदर्भ के लिए lock(object)एक Monitor- देखें MSDN का उपयोग करके यह कथन लागू किया गया है ।

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


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

2
व्यावहारिक लॉक फ्री प्रोग्रामिंग शांगरी ला की तरह है, सिवाय इसके मौजूद है। केइर फ्रेजर का पेपर (पीडीएफ) इसकी बजाय दिलचस्प तरीके से (2004 में वापस जा रहा है) की पड़ताल करता है। और हम अभी भी 2012 में इसके साथ संघर्ष कर रहे हैं। हम चूसते हैं।
टिम पोस्ट

22

अन्य उत्तरों के अलावा, निम्न विवरण खिड़कियों पर महत्वपूर्ण खंडों के लिए विशिष्ट हैं:

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

लिनक्स में, मुझे लगता है कि उनके पास एक "स्पिन लॉक" है जो स्पिन गणना के साथ महत्वपूर्ण खंड के समान उद्देश्य को पूरा करता है।


दुर्भाग्य से एक विंडो क्रिटिकल सेक्शन में कर्नेल मोड में CAS ऑपरेशन करना शामिल है , जो वास्तविक इंटरलॉक किए गए ऑपरेशन की तुलना में बड़े पैमाने पर महंगा है। साथ ही, विंडोज के महत्वपूर्ण खंडों में उनके साथ जुड़े स्पिन काउंट हो सकते हैं।
Promit

2
यह निश्चित रूप से सच नहीं है। कैस को यूजर मोड में cmpxchg के साथ किया जा सकता है।
माइकल

मुझे लगा कि अगर आप InitializeCriticalSection कहते हैं तो डिफॉल्ट स्पिन काउंट शून्य था - अगर आपको स्पिन काउंट लागू करना है तो आपको InitializeCriticalSectionAndSpinCount पर कॉल करना होगा। क्या आपके पास इसके लिए कोई संदर्भ है?
1800 जानकारी

18

क्रिटिकल सेक्शन और म्यूटेक्स ऑपरेटिंग सिस्टम विशिष्ट नहीं हैं, उनकी मल्टीथ्रेडिंग / मल्टीप्रोसेसिंग की अवधारणाएं हैं।

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

Mutex * Mutex महत्वपूर्ण अनुभाग कोड को लागू करने का एक तरीका है (इसे टोकन की तरह समझें ... महत्वपूर्ण-section_code को चलाने के लिए धागे के पास इसका अधिकार होना चाहिए)


2
इसके अलावा, म्यूटेक्स को प्रक्रियाओं के दौरान साझा किया जा सकता है।
विन्यासकर्ता

14

एक म्यूटेक्स एक ऐसी वस्तु है जिसे एक धागा प्राप्त कर सकता है, अन्य धागों को प्राप्त करने से रोकता है। यह सलाहकार है, अनिवार्य नहीं है; एक धागा संसाधन का उपयोग कर सकता है म्यूटेक्स इसे प्राप्त किए बिना प्रतिनिधित्व करता है।

एक महत्वपूर्ण खंड एक कोड की लंबाई है जिसे ऑपरेटिंग सिस्टम द्वारा बाधित नहीं होने की गारंटी है। छद्म कोड में, यह इस तरह होगा:

StartCriticalSection();
    DoSomethingImportant();
    DoSomeOtherImportantThing();
EndCriticalSection();

1
मुझे लगता है कि पोस्टर यूजर मोड सिंक्रोनाइज़ेशन प्राइमिटिव्स के बारे में बात कर रहा था, जैसे कि win32 क्रिटिकल सेक्शन ऑब्जेक्ट, जो सिर्फ म्यूचुअल एक्सक्लूज़न प्रदान करता है। मैं लिनक्स के बारे में नहीं जानता, लेकिन विंडोज कर्नेल में महत्वपूर्ण क्षेत्र हैं जो आपके द्वारा वर्णित व्यवहार की तरह व्यवहार करते हैं - गैर-रुकावट।
माइकल

1
मैं नहीं जानता कि आप क्यों अपमानित हुए। एक महत्वपूर्ण खंड की अवधारणा है, जिसे आपने सही ढंग से वर्णित किया है, जो कि क्रिटिकल स्नेह नामक विंडोज कर्नेल ऑब्जेक्ट से अलग है, जो एक प्रकार का म्यूटेक्स है। मेरा मानना ​​है कि ओपी बाद की परिभाषा के बारे में पूछ रहा था।
एडम रोसेनफील्ड

कम से कम मैं भाषा अज्ञेय टैग से भ्रमित हो गया। लेकिन किसी भी मामले में यह है कि हम Microsoft को उनके कार्यान्वयन को उनके आधार वर्ग के समान नाम देने के लिए प्राप्त करते हैं। बुरा कोडिंग अभ्यास!
मिकको रेंटेनन

खैर, उन्होंने यथासंभव विस्तार के लिए कहा, और विशेष रूप से कहा विंडोज और लिनक्स इसलिए लगता है जैसे अवधारणाएं अच्छी हैं। +1 - -1 को भी नहीं समझ पाया: /
जेसन कोको

14

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

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

दुर्भाग्य से, futexes को लागू करने के लिए बहुत मुश्किल हो सकता है (पीडीएफ)। (2018 अपडेट, वे लगभग डरावने नहीं हैं क्योंकि वे 2009 में थे)।

इसके अलावा, दोनों प्लेटफार्मों में बहुत अधिक समान है। आप साझा संरचना में परमाणु, टोकन संचालित अपडेट इस तरीके से कर रहे हैं कि (उम्मीद है) भुखमरी का कारण न बने। जो बचता है, बस उसे पूरा करने की विधि है।


6

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


6

मेरे 2 सेंट जोड़ने के लिए, महत्वपूर्ण अनुभागों को एक संरचना के रूप में परिभाषित किया गया है और उन पर संचालन उपयोगकर्ता-मोड संदर्भ में किया जाता है।

ntdll! _RTL_CRITICAL_SECTION
   + 0x000 डीबगइन्फो: Ptr32 _RTL_CRITICAL_SECTION_DEBUG
   + 0x004 लॉककाउंट: Int4B
   + 0x008 पुनरावर्तन करें: Int4B
   + 0x00c का मालिक है: Ptr32 शून्य
   + 0x010 लॉकस्मैफोर: Ptr32 शून्य
   + 0x014 स्पिनकाउंट: Uint4B

जबकि म्यूटेक्स विंडोज ऑब्जेक्ट डायरेक्टरी में बनाए गए कर्नेल ऑब्जेक्ट (ExMutantObjectType) हैं। म्यूटेक्स ऑपरेशन ज्यादातर कर्नेल-मोड में लागू किए जाते हैं। उदाहरण के लिए, एक म्यूटेक्स बनाते समय, आप अंत में कर्नेल में nt! NtCreateMutant कहते हैं।


क्या होता है जब एक प्रोग्राम जो Mutex ऑब्जेक्ट को आरंभ और उपयोग करता है, क्रैश करता है? क्या म्यूटेक्स ऑब्जेक्ट स्वचालित रूप से निपटा जाता है? नहीं, मैं कहूंगा। सही?
अंकुर

6
कर्नेल ऑब्जेक्ट में एक संदर्भ गणना होती है। किसी ऑब्जेक्ट को हैंडल बंद करने से रेफरेंस काउंट कम हो जाता है और जब यह 0 पर पहुंच जाता है तो ऑब्जेक्ट फ्री हो जाता है। जब कोई प्रक्रिया क्रैश हो जाती है, तो उसके सभी हैंडल स्वचालित रूप से बंद हो जाते हैं, इसलिए एक म्यूटेक्स कि केवल उस प्रक्रिया को हैंडल करने के लिए स्वचालित रूप से निपटाया जाएगा।
माइकल

और यही कारण है कि क्रिटिकल सेक्शन ऑब्जेक्ट्स प्रोसेस बाउंड होते हैं, दूसरी तरफ म्यूटेक्स को प्रोसेस में शेयर किया जा सकता है।
सिसिर

2

माइकल से बढ़िया जवाब। मैंने C ++ 11 में पेश किए गए म्यूटेक्स वर्ग के लिए तीसरा परीक्षण जोड़ा है। परिणाम कुछ दिलचस्प है, और अभी भी एकल प्रक्रियाओं के लिए CRITICAL_SECTION वस्तुओं के अपने मूल समर्थन का समर्थन करता है।

mutex m;
HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
CRITICAL_SECTION critSec;
InitializeCriticalSection(&critSec);

LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
LARGE_INTEGER start, end;

// Force code into memory, so we don't see any effects of paging.
EnterCriticalSection(&critSec);
LeaveCriticalSection(&critSec);
QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    EnterCriticalSection(&critSec);
    LeaveCriticalSection(&critSec);
}

QueryPerformanceCounter(&end);

int totalTimeCS = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

// Force code into memory, so we don't see any effects of paging.
WaitForSingleObject(mutex, INFINITE);
ReleaseMutex(mutex);

QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    WaitForSingleObject(mutex, INFINITE);
    ReleaseMutex(mutex);
}

QueryPerformanceCounter(&end);

int totalTime = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);

// Force code into memory, so we don't see any effects of paging.
m.lock();
m.unlock();

QueryPerformanceCounter(&start);
for (int i = 0; i < 1000000; i++)
{
    m.lock();
    m.unlock();
}

QueryPerformanceCounter(&end);

int totalTimeM = (int)((end.QuadPart - start.QuadPart) * 1000 / freq.QuadPart);


printf("C++ Mutex: %d Mutex: %d CritSec: %d\n", totalTimeM, totalTime, totalTimeCS);

मेरे परिणाम 217, 473 और 19 थे (ध्यान दें कि पिछले दो बार के मेरे अनुपात माइकल की तुलना में लगभग बराबर हैं, लेकिन मेरी मशीन उनके मुकाबले कम से कम चार साल छोटी है, इसलिए आप 2009 और 2013 के बीच बढ़ी हुई गति के प्रमाण देख सकते हैं। , जब XPS-8700 बाहर आया)। नया म्यूटेक्स वर्ग विंडोज म्यूटेक्स की तुलना में दोगुना है, लेकिन अभी भी विंडोज CRITICAL_SECTION ऑब्जेक्ट की गति से दसवें से कम है। ध्यान दें कि मैंने केवल गैर-पुनरावर्ती म्यूटेक्स का परीक्षण किया था। CRITICAL_SECTION ऑब्जेक्ट पुनरावर्ती हैं (एक धागा उन्हें बार-बार दर्ज कर सकता है, बशर्ते वह समान संख्या में छोड़ देता है)।


0

यदि इसे अपने वास्तविक मापदंडों का उपयोग किया जाता है, तो एसी फ़ंक्शन को रीएन्टेंट कहा जाता है।

एक ही समय में कई थ्रेड्स द्वारा रेंटेंट फ़ंक्शन को बुलाया जा सकता है।

रीक्रेंट फंक्शन का उदाहरण:

int reentrant_function (int a, int b)
{
   int c;

   c = a + b;

   return c;
}

गैर प्रतिवादी समारोह का उदाहरण:

int result;

void non_reentrant_function (int a, int b)
{
   int c;

   c = a + b;

   result = c;

}

C मानक लाइब्रेरी स्ट्रेटोक () एक बार में नहीं है और एक ही समय में 2 या अधिक थ्रेड द्वारा उपयोग नहीं किया जा सकता है।

कुछ प्लेटफ़ॉर्म SDK, strtok () जिसे strtok_r () कहा जाता है, के रीक्रेंटली संस्करण के साथ आता है;

एनरिको मिग्लोर

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