मॉनिटर बनाम लॉक


88

जब C # में थ्रेड सुरक्षा के लिए Monitorकक्षा या lockकीवर्ड का उपयोग करना उचित है ?

संपादित करें: यह अब तक के उत्तरों से लगता है कि कक्षा lockमें कॉल की एक श्रृंखला के लिए संक्षिप्त हाथ है Monitor। वास्तव में लॉक कॉल शॉर्ट-हैंड के लिए क्या है? या अधिक स्पष्ट रूप से,

class LockVsMonitor
{
    private readonly object LockObject = new object();
    public void DoThreadSafeSomethingWithLock(Action action)
    {
        lock (LockObject)
        {
            action.Invoke();
        }
    }
    public void DoThreadSafeSomethingWithMonitor(Action action)
    {
        // What goes here ?
    }
}

अपडेट करें

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

जवाबों:


89

एरिक लिपर्ट अपने ब्लॉग में इस बारे में बात करते हैं: ताले और अपवाद मिश्रण नहीं करते हैं

समतुल्य कोड C # 4.0 और पुराने संस्करणों के बीच भिन्न होता है।


C # 4.0 में यह है:

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    { body }
}
finally
{
    if (lockWasTaken) Monitor.Exit(temp);
}

यह Monitor.Enterलॉक होने पर ध्वज को स्थापित करने के लिए परमाणु रूप से निर्भर करता है।


और पहले यह था:

var temp = obj;
Monitor.Enter(temp);
try
{
   body
}
finally
{
    Monitor.Exit(temp);
}

इस पर निर्भर करता है कोई अपवाद नहीं के बीच फेंक दिया जा रहा है Monitor.Enterऔर try। मुझे लगता है कि डिबग कोड में इस शर्त का उल्लंघन किया गया था क्योंकि संकलक ने उनके बीच एक एनओपी डाला और इस तरह उन लोगों के बीच धागा गर्भपात कराया।


जैसा कि मैंने कहा कि पहला उदाहरण C # 4 है और दूसरा वह है जो पहले के संस्करणों का उपयोग करता है।
कोडइन्चोस

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

5
साझा राज्य को पुनर्स्थापित करने वाला आईएमओ लॉकिंग / मल्टी-थ्रेडिंग के लिए ऑर्थोगोनल है। तो यह एक कोशिश-कैच के साथ किया जाना चाहिए / अंत में lockब्लॉक के अंदर ।
कोडइन्चोस

2
@ kizzx2: पाठक-लेखक के ताले के साथ ऐसा पैटर्न विशेष रूप से अच्छा होगा। अगर कोई अपवाद एक पाठक लॉक रखने वाले कोड के भीतर होता है, तो यह उम्मीद करने का कोई कारण नहीं है कि संरक्षित संसाधन क्षतिग्रस्त हो सकता है, और इस तरह इसे अमान्य करने का कोई कारण नहीं है। यदि लेखक लॉक के भीतर अपवाद होता है और अपवाद-हैंडलिंग कोड स्पष्ट रूप से इंगित नहीं करता है कि संरक्षित ऑब्जेक्ट की स्थिति की मरम्मत की गई है, तो यह सुझाव देगा कि ऑब्जेक्ट क्षतिग्रस्त हो सकता है और अमान्य होना चाहिए। IMHO, अप्रत्याशित अपवाद किसी प्रोग्राम को क्रैश नहीं करना चाहिए, लेकिन ऐसा कुछ भी अमान्य होना चाहिए जो भ्रष्ट हो सकता है।
सुपरकैट

2
@ArsenZahray आपको Pulseसाधारण लॉकिंग की आवश्यकता नहीं है । यह कुछ उन्नत बहु-सूत्रण परिदृश्यों में महत्वपूर्ण है। मैंने कभी Pulseसीधे इस्तेमाल नहीं किया ।
कोडइन्चोस

43

lockबस के लिए शॉर्टकट है Monitor.Enterसाथ try+ finallyऔर Monitor.Exit। जब भी यह पर्याप्त हो तो लॉक स्टेटमेंट का उपयोग करें - अगर आपको ट्राइएंटर जैसी किसी चीज़ की आवश्यकता है, तो आपको मॉनिटर का उपयोग करना होगा।


23

एक लॉक स्टेटमेंट इसके बराबर है:

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

हालांकि, ध्यान रखें कि मॉनिटर प्रतीक्षा () और पल्स () भी कर सकता है , जो अक्सर जटिल मल्टीथ्रेडिंग स्थितियों में उपयोगी होते हैं।

अपडेट करें

हालांकि C # 4 में इसके अलग तरीके से लागू किया गया:

bool lockWasTaken = false;
var temp = obj;
try 
{
     Monitor.Enter(temp, ref lockWasTaken); 
     //your code
}
finally 
{ 
     if (lockWasTaken) 
             Monitor.Exit(temp); 
} 

टिप्पणियों और लिंक के लिए कोडइनचेक्स से थैंक्स


C # 4 में लॉक स्टेटमेंट को अलग तरीके से लागू किया जाता है। blogs.msdn.com/b/ericlippert/archive/2009/03/06/…
CodesInChaos

14

Monitorअधिक लचीला है। मॉनिटर का उपयोग करने का मेरा पसंदीदा उपयोग मामला है जब आप अपनी बारी का इंतजार नहीं करना चाहते हैं और बस छोड़ दें :

//already executing? forget it, lets move on
if(Monitor.TryEnter(_lockObject))
{
    //do stuff;
    Monitor.Exit(_lockObject);
}

6

जैसा कि दूसरों ने कहा है, के lockलिए "समतुल्य" है

Monitor.Enter(object);
try
{
   // Your code here...
}
finally
{
   Monitor.Exit(object);
}

लेकिन सिर्फ जिज्ञासा से बाहर, lockआपके पास जाने वाले पहले संदर्भ को संरक्षित करेगा और यदि आप इसे बदलते हैं तो इसे फेंक नहीं देंगे। मुझे पता है कि यह लॉक की गई वस्तु को बदलने की सिफारिश नहीं की गई है और आप इसे नहीं करना चाहते हैं।

लेकिन फिर से, विज्ञान के लिए, यह ठीक काम करता है:

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        lock (lockObject)
        {
            lockObject += "x";
        }
    }));
Task.WaitAll(tasks.ToArray());

... और यह नहीं है:

var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
    tasks.Add(Task.Run(() =>
    {
        Thread.Sleep(250);
        Monitor.Enter(lockObject);
        try
        {
            lockObject += "x";
        }
        finally
        {
            Monitor.Exit(lockObject);
        }
    }));
Task.WaitAll(tasks.ToArray());

त्रुटि:

70783sTUDIES.exe में 'System.Threading.SynchronizationLockException' प्रकार का एक अपवाद हुआ, लेकिन उपयोगकर्ता कोड में संभाला नहीं गया था

अतिरिक्त जानकारी: ऑब्जेक्ट सिंक्रोनाइज़ेशन विधि कोड के एक गैर-सिंक्रनाइज़ किए गए ब्लॉक से कॉल की गई थी।

इसका कारण यह है कि जिस Monitor.Exit(lockObject);पर कार्य lockObjectकिया गया है क्योंकि stringsअपरिवर्तनीय है, तो आप इसे कोड के एक असंबद्ध ब्लॉक से बुला रहे हैं .. लेकिन फिर भी। यह सिर्फ एक मजेदार तथ्य है।


"यह इसलिए है क्योंकि Monitor.Exit (lockObject); लॉकऑब्जेक्ट पर कार्य करेगा"। फिर ताला वस्तु के साथ कुछ भी नहीं करता है? ताला कैसे काम करता है?
युगो अमिरल

@ युगोअमेरिल, मुझे लगता है कि यह इसलिए है क्योंकि लॉक स्टेटमेंट पहले पारित संदर्भ को ध्यान में रखता है और फिर बदले हुए संदर्भ का उपयोग करने के बजाय इसका उपयोग करता है, जैसे:object temp = lockObject; Monitor.Enter(temp); <...locked code...> Monitor.Exit(temp);
ज़्यूरवले ए।

3

दोनों एक ही चीज हैं। लॉक c शार्प कीवर्ड है और मॉनिटर क्लास का उपयोग करता है।

http://msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx


3
Msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx पर देखें "वास्तव में, लॉक कीवर्ड मॉनिटर क्लास के साथ लागू किया जाता है। उदाहरण के लिए"
RobertoBr

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

3

मॉनिटर का लॉक और बेसिक व्यवहार (एंटर + एग्जिट) कमोबेश एक जैसा है, लेकिन मॉनिटर के पास अधिक विकल्प हैं जो आपको अधिक सिंक्रोनाइज़ेशन की संभावनाएं प्रदान करते हैं।

ताला एक शॉर्टकट है, और यह मूल उपयोग के लिए विकल्प है।

यदि आपको अधिक नियंत्रण की आवश्यकता है, तो मॉनिटर बेहतर विकल्प है। आप उन्नत उपयोगों (जैसे अवरोधों, सेमाफ़ोर्स और इसी तरह) के लिए प्रतीक्षा, ट्राइटर और पल्स का उपयोग कर सकते हैं।


1

ताला लॉक कीवर्ड यह सुनिश्चित करता है कि एक समय में एक धागा कोड के एक टुकड़े को निष्पादित कर रहा है।

ताला (lockObject)

        {
        //   Body
        }

लॉक कीवर्ड किसी स्टेटमेंट ब्लॉक को किसी दिए गए ऑब्जेक्ट के लिए म्यूचुअल-एक्सक्लूजन लॉक प्राप्त करके, स्टेटमेंट को निष्पादित करने और फिर लॉक को रिलीज़ करके एक महत्वपूर्ण खंड के रूप में चिह्नित करता है।

यदि कोई अन्य थ्रेड लॉक कोड दर्ज करने का प्रयास करता है, तो वह तब तक प्रतीक्षा करेगा, जब तक कि ऑब्जेक्ट जारी न हो जाए।

मॉनिटर मॉनिटर एक स्थिर वर्ग है और System.Threading नाम स्थान से संबंधित है।

यह ऑब्जेक्ट पर अनन्य लॉक प्रदान करता है ताकि किसी भी बिंदु पर केवल एक धागा महत्वपूर्ण खंड में प्रवेश कर सके।

C # में मॉनिटर और लॉक के बीच अंतर

ताला मॉनिटर के लिए शॉर्टकट है। कोशिश और अंत में साथ दें। लॉक हैंडल कोशिश करते हैं और अंत में आंतरिक रूप से लॉक होते हैं लॉक = मॉनिटर + अंत में प्रयास करें।

यदि आप उन्नत मल्टीथ्रेडिंग समाधानों का उपयोग करने के लिए अधिक नियंत्रण चाहते हैं TryEnter() Wait() , Pulse()औरPulseAll() विधियों, तो मॉनिटर वर्ग अपने विकल्प है।

C # Monitor.wait(): एक थ्रेड अन्य थ्रेड्स को सूचित करने के लिए प्रतीक्षा करता है।

Monitor.pulse(): एक थ्रेड दूसरे थ्रेड को सूचित करता है।

Monitor.pulseAll(): एक थ्रेड एक प्रक्रिया के भीतर अन्य सभी थ्रेड्स को सूचित करता है


0

उपरोक्त सभी स्पष्टीकरणों के अलावा, लॉक एक C # स्टेटमेंट है जबकि मॉनिटर .NET का एक वर्ग है जो System.Threading नामस्थान में स्थित है।

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