लॉक (यह) {…} खराब क्यों है?


484

MSDN प्रलेखीकरण का कहना है कि

public class SomeObject
{
  public void SomeOperation()
  {
    lock(this)
    {
      //Access instance variables
    }
  }
}

"एक समस्या है अगर उदाहरण सार्वजनिक रूप से पहुँचा जा सकता है"। मैं सोच रहा हूँ क्यों? क्या यह इसलिए है क्योंकि ताला आवश्यकता से अधिक समय तक आयोजित किया जाएगा? या कुछ और कपटी कारण है?

जवाबों:


508

यह thisलॉक स्टेटमेंट में उपयोग करने के लिए खराब रूप है क्योंकि यह आम तौर पर आपके नियंत्रण से बाहर है जो उस वस्तु पर लॉक हो सकता है।

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

एक निजी क्षेत्र आमतौर पर एक बेहतर विकल्प होता है क्योंकि कंपाइलर इसके लिए एक्सेस प्रतिबंध लागू करेगा, और यह लॉकिंग तंत्र को अतिक्रमण करेगा। thisअपने लॉकिंग कार्यान्वयन के भाग को जनता के सामने लाने से उल्लंघन का उपयोग करना । यह भी स्पष्ट नहीं है कि thisजब तक यह दस्तावेज नहीं किया गया है, तब तक आप एक ताला प्राप्त करेंगे । फिर भी, किसी समस्या को रोकने के लिए प्रलेखन पर निर्भर होना उप-इष्टतम है।

अंत में, आम गलतफहमी है कि lock(this)वास्तव में पारित वस्तु को एक पैरामीटर के रूप में संशोधित किया जाता है, और किसी तरह से यह केवल पढ़ने योग्य या अप्राप्य बनाता है। यह झूठा हैlockकेवल एक कुंजी के रूप में कार्य करने के लिए पैरामीटर के रूप में पारित वस्तु । यदि उस ताले पर पहले से ही ताला लगा है, तो ताला नहीं बनाया जा सकता है; अन्यथा, लॉक की अनुमति है।

यही कारण है कि lockस्टेटमेंट में कुंजियों के रूप में स्ट्रिंग का उपयोग करना बुरा है , क्योंकि वे अपरिवर्तनीय हैं और आवेदन के कुछ हिस्सों में साझा / सुलभ हैं। आपको इसके बजाय एक निजी चर का उपयोग करना चाहिए, एक Objectउदाहरण अच्छी तरह से करेगा।

उदाहरण के रूप में निम्न C # कोड चलाएँ।

public class Person
{
    public int Age { get; set;  }
    public string Name { get; set; }

    public void LockThis()
    {
        lock (this)
        {
            System.Threading.Thread.Sleep(10000);
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        var nancy = new Person {Name = "Nancy Drew", Age = 15};
        var a = new Thread(nancy.LockThis);
        a.Start();
        var b = new Thread(Timewarp);
        b.Start(nancy);
        Thread.Sleep(10);
        var anotherNancy = new Person { Name = "Nancy Drew", Age = 50 };
        var c = new Thread(NameChange);
        c.Start(anotherNancy);
        a.Join();
        Console.ReadLine();
    }

    static void Timewarp(object subject)
    {
        var person = subject as Person;
        if (person == null) throw new ArgumentNullException("subject");
        // A lock does not make the object read-only.
        lock (person.Name)
        {
            while (person.Age <= 23)
            {
                // There will be a lock on 'person' due to the LockThis method running in another thread
                if (Monitor.TryEnter(person, 10) == false)
                {
                    Console.WriteLine("'this' person is locked!");
                }
                else Monitor.Exit(person);
                person.Age++;
                if(person.Age == 18)
                {
                    // Changing the 'person.Name' value doesn't change the lock...
                    person.Name = "Nancy Smith";
                }
                Console.WriteLine("{0} is {1} years old.", person.Name, person.Age);
            }
        }
    }

    static void NameChange(object subject)
    {
        var person = subject as Person;
        if (person == null) throw new ArgumentNullException("subject");
        // You should avoid locking on strings, since they are immutable.
        if (Monitor.TryEnter(person.Name, 30) == false)
        {
            Console.WriteLine("Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string \"Nancy Drew\".");
        }
        else Monitor.Exit(person.Name);

        if (Monitor.TryEnter("Nancy Drew", 30) == false)
        {
            Console.WriteLine("Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!");
        }
        else Monitor.Exit("Nancy Drew");
        if (Monitor.TryEnter(person.Name, 10000))
        {
            string oldName = person.Name;
            person.Name = "Nancy Callahan";
            Console.WriteLine("Name changed from '{0}' to '{1}'.", oldName, person.Name);
        }
        else Monitor.Exit(person.Name);
    }
}

कंसोल आउटपुट

'this' person is locked!
Nancy Drew is 16 years old.
'this' person is locked!
Nancy Drew is 17 years old.
Failed to obtain lock on 50 year old Nancy, because Timewarp(object) locked on string "Nancy Drew".
'this' person is locked!
Nancy Smith is 18 years old.
'this' person is locked!
Nancy Smith is 19 years old.
'this' person is locked!
Nancy Smith is 20 years old.
Failed to obtain lock using 'Nancy Drew' literal, locked by 'person.Name' since both are the same object thanks to inlining!
'this' person is locked!
Nancy Smith is 21 years old.
'this' person is locked!
Nancy Smith is 22 years old.
'this' person is locked!
Nancy Smith is 23 years old.
'this' person is locked!
Nancy Smith is 24 years old.
Name changed from 'Nancy Drew' to 'Nancy Callahan'.

2
जैसा कि मैंने कहा: (1) नैन्सी थ्रेड 1 (लॉक) के साथ थ्रेड 1 में है। (2) एसएएमई नैन्सी थ्रेड 2 में बुढ़ापा है जबकि थ्रेड 1 में बंद है - एक बंद वस्तु साबित करना केवल-पढ़ने के लिए नहीं है। ALSO (2a) जबकि थ्रेड 2 में, यह नैन्सी ऑब्जेक्ट भी नाम पर लॉक है। (३) एक ही नाम से अलग वस्तु बनाएँ । (4) थ्रेड 3 में पास करें और नाम के साथ लॉक करने का प्रयास करें। (बिग फिनिश) BUT "स्ट्रिंग्स अपरिवर्तनीय हैं" जिसका अर्थ है किसी भी ऑब्जेक्ट को स्ट्रिंग "नैन्सी ड्रू" का संदर्भ देते हुए देखा जा रहा है, यह वास्तव में मेमोरी में समान स्ट्रिंग उदाहरण है। जब ऑब्जेक्ट 1 को उसी मान पर लॉक किया जाता है, तो ऑब्जेक्ट 2 को एक स्ट्रिंग पर लॉक नहीं मिल सकता है
रडारबॉब

lock(this)मानक सलाह के बजाय एक मानक चर का उपयोग करना ; यह ध्यान रखना महत्वपूर्ण है कि ऐसा करने से आमतौर पर बाहर के कोड के लिए असंभव हो सकता है क्योंकि विधि कॉल के बीच में रखी जाने वाली वस्तु के साथ जुड़े लॉक का कारण बनता है। यह अच्छी बात हो सकती है या नहीं भी । बाहरी कोड को मनमानी अवधि के लिए लॉक रखने की अनुमति देने में कुछ खतरा है, और कक्षाएं आमतौर पर इस तरह के उपयोग को अनावश्यक बनाने के लिए डिज़ाइन की जानी चाहिए, लेकिन हमेशा व्यावहारिक विकल्प नहीं होते हैं। एक सरल उदाहरण के रूप में, जब तक कि कोई संग्रह स्वयं की एक विधि ToArrayया ToListविधि को लागू नहीं करता ...
Supercat

4
(जैसा कि `IEnumerable <T> विस्तार विधियों के विपरीत), एक थ्रेड के लिए एकमात्र तरीका जो संग्रह का एक स्नैपशॉट प्राप्त करना चाहता है वह सभी परिवर्तनों को लॉक करते समय इसे एन्यूमरेट कर सकता है । ऐसा करने के लिए, इसमें किसी भी कोड द्वारा अधिग्रहित एक लॉक का एक्सेस होना चाहिए जो संग्रह को बदल देगा। लॉक को उजागर करने में विफलता से यह असंभव हो सकता है जैसे कि कार्यक्रम समय-समय पर संग्रह का एक अतुल्यकालिक स्नैपशॉट करता है (जैसे संग्रह-ब्राउज़िंग उपयोगकर्ता इंटरफ़ेस को अपडेट करने के लिए)।
सुपरकैट

there is the common misconception that lock(this) actually modifies the object passed as a parameter, and in some way makes it read-only or inaccessible. This is false- मुझे विश्वास है कि ये वार्ता CLR ऑब्जेक्ट में सिंकब्लॉक बिट के बारे में हैं, इसलिए औपचारिक रूप से यह सही है - संशोधित संशोधित ऑब्जेक्ट ही
sll

@Esteban, मैं आपके उदाहरण से बिल्कुल प्यार करता हूं, यह बहुत बढ़िया है। मेरे पास आपके लिए एक सवाल है। विधि का आपका कोड NameChange (..) के साथ समाप्त होता है: <code> if (Monitor.TryEnter (person.Name, 10000)) {। । । } और मॉनिटर। एक्सिट (person.Name); </ कोड> क्या यह समाप्त नहीं होना चाहिए: <code> if (Monitor.TryEnter (person.Name, 10000)) {। । । Monitor.Exit (person.Name); } </ code>
AviFarah

64

क्योंकि यदि लोग आपके ऑब्जेक्ट उदाहरण (यानी: आपके this) सूचक पर प्राप्त कर सकते हैं , तो वे उसी ऑब्जेक्ट को लॉक करने का भी प्रयास कर सकते हैं। अब उन्हें इस बात की जानकारी नहीं हो सकती है कि आप thisआंतरिक रूप से ताला लगा रहे हैं , इसलिए इससे समस्याएँ उत्पन्न हो सकती हैं (संभवतः गतिरोध)

इसके अलावा, यह बुरा अभ्यास भी है, क्योंकि यह "बहुत अधिक" लॉक कर रहा है

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


44
इस उत्तर का अंतिम पैराग्राफ सही नहीं है। लॉक किसी भी तरह से ऑब्जेक्ट को अप्राप्य या केवल पढ़ने योग्य नहीं बनाता है। लॉक (यह) किसी अन्य थ्रेड को इसके द्वारा संदर्भित ऑब्जेक्ट को कॉल या संशोधित करने से नहीं रोकता है।
एस्टेबन ब्रेनस

3
यह तब होता है जब अन्य तरीकों को भी बुलाया जा रहा है एक ताला (यह)। मुझे विश्वास है कि वह जो बिंदु बना रहा था। "यदि आप अपने फ़ंक्शन में संपूर्ण ऑब्जेक्ट को लॉक करते हैं" तो नोटिस करें ...
Herms

@ कथन: यह स्पष्ट है। @Herms: हाँ, लेकिन आपको उस कार्यक्षमता को प्राप्त करने के लिए 'इस' का उपयोग करने की आवश्यकता नहीं है, सूचियों में SyncRoot गुण उस उद्देश्य को पूरा करता है, उदाहरण के लिए, जबकि यह स्पष्ट तुल्यकालन उस कुंजी पर किया जाना चाहिए।
एस्टेबन ब्रेनस

पुन: "बहुत अधिक" लॉकिंग: यह एक अच्छा संतुलन कार्य है जो यह तय करता है कि ताला क्या है। विदित हो कि लॉक लेने पर कैश-फ्लश CPU संचालन शामिल होता है और यह कुछ महंगा होता है। दूसरे शब्दों में: प्रत्येक व्यक्तिगत पूर्णांक को लॉक और अपडेट न करें। :)
ज़ैन लिंक्स

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

44

MSDN विषय धागा तुल्यकालन पर एक नज़र डालें (C # प्रोग्रामिंग गाइड)

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


34

मुझे पता है कि यह एक पुराना धागा है, लेकिन क्योंकि लोग अभी भी इसे देख सकते हैं और इस पर भरोसा कर सकते हैं, यह इंगित करना महत्वपूर्ण है कि lock(typeof(SomeObject))इससे भी बदतर है lock(this)। यह कहने के बाद; lock(typeof(SomeObject))बुरे व्यवहार की ओर इशारा करने के लिए एलन के लिए ईमानदार कुदोस ।

का एक उदाहरण System.Typeसबसे सामान्य, मोटे अनाज वाली वस्तुओं में से एक है। बहुत कम से कम, System.Type का एक उदाहरण AppDomain के लिए वैश्विक है, और .NET एक AppDomain में कई प्रोग्राम चला सकता है। इसका मतलब है कि दो पूरी तरह से अलग-अलग कार्यक्रम संभावित रूप से एक दूसरे में हस्तक्षेप का कारण बन सकते हैं यहां तक ​​कि गतिरोध पैदा करने की हद तक अगर वे दोनों एक ही प्रकार के उदाहरण पर सिंक्रनाइज़ेशन लॉक प्राप्त करने का प्रयास करते हैं।

तो lock(this)विशेष रूप से मजबूत रूप नहीं है, समस्याओं का कारण बन सकता है और हमेशा सभी कारणों का हवाला देते हुए भौहें बढ़ाना चाहिए। फिर भी व्यापक रूप से उपयोग किया जाता है, अपेक्षाकृत अच्छी तरह से सम्मानित और जाहिरा तौर पर स्थिर कोड जैसे log4net जो लॉक (इस) पैटर्न का बड़े पैमाने पर उपयोग करता है, भले ही मैं व्यक्तिगत रूप से उस पैटर्न को देखना पसंद करूं।

लेकिन lock(typeof(SomeObject))कीड़े के एक पूरे नए और बढ़ाया कैन को खोलता है।

हांलांकि इसकी कीमत के बारे निश्चित नहीं हूँ।


26

... और ठीक यही तर्क इस निर्माण पर भी लागू होते हैं:

lock(typeof(SomeObject))

17
लॉक (टाइपऑफ़ (SomeObject)) वास्तव में लॉक (यह) ( stackoverflow.com/a/10510647/618649 ) की तुलना में बहुत खराब है ।
क्रेग

1
ठीक है, लॉक (Application.Current) तो और भी बुरा है, लेकिन फिर भी इन बेवकूफ चीजों में से कौन कोशिश करेगा? ताला (यह) तार्किक और आत्मघाती लगता है, लेकिन ये अन्य उदाहरण नहीं हैं।
ज़ार शारदान

मैं इस बात से सहमत नहीं हूं कि lock(this)विशेष रूप से तार्किक और रसीला लगता है। यह एक भयानक मोटे ताला है, और कोई भी अन्य कोड आपके ऑब्जेक्ट पर लॉक लगा सकता है, संभवतः आपके आंतरिक कोड में हस्तक्षेप हो सकता है। अधिक दानेदार ताले लें, और सख्त नियंत्रण ग्रहण करें। क्या lock(this)इसके लिए जा रहा है कि यह एक बहुत से बेहतर है lock(typeof(SomeObject))
क्रेग

8

कल्पना कीजिए कि आपके कार्यालय में एक कुशल सचिव है जो विभाग में एक साझा संसाधन है। एक बार में, आप उनकी ओर भागते हैं क्योंकि आपके पास एक कार्य है, केवल यह आशा करने के लिए कि आपके किसी अन्य सहकर्मी ने पहले ही दावा नहीं किया है। आमतौर पर आपको केवल कुछ समय के लिए रुकना होता है।

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

lock(this)जैसा हमने देखा है वैसा ही बुरा है। कोई बाहरी वस्तु ऑब्जेक्ट पर लॉक हो सकती है और चूंकि आप नियंत्रित नहीं करते हैं कि क्लास का उपयोग कौन कर रहा है, कोई भी उस पर लॉक कर सकता है ... जो कि ऊपर वर्णित सटीक उदाहरण है। फिर, समाधान वस्तु के एक्सपोजर को सीमित करना है। हालांकि, अगर आप एक है, तो private, protectedया internalवर्ग आप पहले से ही नियंत्रित कर सकता है, जो आपके वस्तु पर ताला लगा है , क्योंकि आप यह सुनिश्चित करें कि आप अपने कोड अपने आप को लिखा है कर रहे हैं। इसलिए यहां संदेश है: इसे उजागर न करें public। इसके अलावा, यह सुनिश्चित करना कि एक ताला इसी तरह के परिदृश्य के गतिरोधों से बचा जाता है।

इसका पूर्ण विपरीत संसाधनों पर लॉक करना है जो पूरे ऐप डोमेन में साझा किए जाते हैं - सबसे खराब स्थिति। यह आपके सचिव को बाहर करने और वहाँ हर किसी को उन्हें दावा करने की अनुमति देने जैसा है। परिणाम एकदम अराजकता है - या स्रोत कोड के संदर्भ में: यह एक बुरा विचार था; इसे फेंक दो और शुरू करो। तो हम इसे कैसे करते हैं?

एप्लिकेशन डोमेन में प्रकार साझा किए जाते हैं क्योंकि यहां अधिकांश लोग इंगित करते हैं। लेकिन वहाँ भी बेहतर चीजें हैं जो हम उपयोग कर सकते हैं: तार। कारण यह है कि तार पूल कर रहे हैं । दूसरे शब्दों में: यदि आपके पास दो स्ट्रिंग्स हैं जो कि एक ऐप डोमेन में समान सामग्री हैं, तो एक मौका है कि उनके पास एक ही पॉइंटर है। चूंकि पॉइंटर को लॉक की के रूप में उपयोग किया जाता है, इसलिए आपको मूल रूप से जो मिलता है वह "अपरिभाषित व्यवहार के लिए तैयार" का एक पर्याय है।

इसी तरह, आपको WCF ऑब्जेक्ट्स, HttpContext.Current, Thread.Current, Singletons (सामान्य रूप से), आदि पर लॉक नहीं करना चाहिए। इन सब से बचने का सबसे आसान तरीका? private [static] object myLock = new object();


3
वास्तव में एक निजी वर्ग होने से समस्या का निवारण नहीं होता है। बाहरी कोड एक निजी वर्ग के उदाहरण के संदर्भ में प्राप्त कर सकता है ...
राशेक

1
@Rashack जब आप तकनीकी रूप से सही होते हैं (उस ओर इशारा करने के लिए +1), मेरी बात यह थी कि आपको नियंत्रण में होना चाहिए जो उदाहरण पर ताला लगा रहा हो। लौटते हुए उदाहरण ऐसे कि टूट जाता है।
atlaste

4

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

इस कोड को देखें कि मेरा क्या मतलब है। कंसोल एप्लिकेशन में अपने मुख्य प्रोग्राम में निम्न कोड जोड़ें:

    static void Main(string[] args)
    {
         TestThreading();
         Console.ReadLine();
    }

    public static void TestThreading()
    {
        Random rand = new Random();
        Thread[] threads = new Thread[10];
        TestLock.balance = 100000;
        for (int i = 0; i < 10; i++)
        {
            TestLock tl = new TestLock();
            Thread t = new Thread(new ThreadStart(tl.WithdrawAmount));
            threads[i] = t;
        }
        for (int i = 0; i < 10; i++)
        {
            threads[i].Start();
        }
        Console.Read();
    }

नीचे की तरह एक नया वर्ग बनाएँ।

 class TestLock
{
    public static int balance { get; set; }
    public static readonly Object myLock = new Object();

    public void Withdraw(int amount)
    {
      // Try both locks to see what I mean
      //             lock (this)
       lock (myLock)
        {
            Random rand = new Random();
            if (balance >= amount)
            {
                Console.WriteLine("Balance before Withdrawal :  " + balance);
                Console.WriteLine("Withdraw        : -" + amount);
                balance = balance - amount;
                Console.WriteLine("Balance after Withdrawal  :  " + balance);
            }
            else
            {
                Console.WriteLine("Can't process your transaction, current balance is :  " + balance + " and you tried to withdraw " + amount);
            }
        }

    }
    public void WithdrawAmount()
    {
        Random rand = new Random();
        Withdraw(rand.Next(1, 100) * 100);
    }
}

यहाँ पर प्रोग्राम लॉकिंग का एक रन है इस

   Balance before Withdrawal :  100000
    Withdraw        : -5600
    Balance after Withdrawal  :  94400
    Balance before Withdrawal :  100000
    Balance before Withdrawal :  100000
    Withdraw        : -5600
    Balance after Withdrawal  :  88800
    Withdraw        : -5600
    Balance after Withdrawal  :  83200
    Balance before Withdrawal :  83200
    Withdraw        : -9100
    Balance after Withdrawal  :  74100
    Balance before Withdrawal :  74100
    Withdraw        : -9100
    Balance before Withdrawal :  74100
    Withdraw        : -9100
    Balance after Withdrawal  :  55900
    Balance after Withdrawal  :  65000
    Balance before Withdrawal :  55900
    Withdraw        : -9100
    Balance after Withdrawal  :  46800
    Balance before Withdrawal :  46800
    Withdraw        : -2800
    Balance after Withdrawal  :  44000
    Balance before Withdrawal :  44000
    Withdraw        : -2800
    Balance after Withdrawal  :  41200
    Balance before Withdrawal :  44000
    Withdraw        : -2800
    Balance after Withdrawal  :  38400

यहाँ myLock पर प्रोग्राम लॉक करने का एक रन है ।

Balance before Withdrawal :  100000
Withdraw        : -6600
Balance after Withdrawal  :  93400
Balance before Withdrawal :  93400
Withdraw        : -6600
Balance after Withdrawal  :  86800
Balance before Withdrawal :  86800
Withdraw        : -200
Balance after Withdrawal  :  86600
Balance before Withdrawal :  86600
Withdraw        : -8500
Balance after Withdrawal  :  78100
Balance before Withdrawal :  78100
Withdraw        : -8500
Balance after Withdrawal  :  69600
Balance before Withdrawal :  69600
Withdraw        : -8500
Balance after Withdrawal  :  61100
Balance before Withdrawal :  61100
Withdraw        : -2200
Balance after Withdrawal  :  58900
Balance before Withdrawal :  58900
Withdraw        : -2200
Balance after Withdrawal  :  56700
Balance before Withdrawal :  56700
Withdraw        : -2200
Balance after Withdrawal  :  54500
Balance before Withdrawal :  54500
Withdraw        : -500
Balance after Withdrawal  :  54000

1
आपके उदाहरण में ध्यान देने योग्य बात क्या है, जैसे कि आप क्या दिखा रहे हैं जो गलत है। जब आप Random rand = new Random();nvm का उपयोग करते हैं तो मुझे यह पता लगाना मुश्किल है कि मुझे क्या लगता है कि मैं बार-बार इसका संतुलन देख रहा
हूं

3

इसके बारे में बहुत अच्छा लेख है http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects द्वारा Rico Mariani, प्रदर्शन वास्तुकार Microsoft® .NET रनटाइम के लिए

अंश:

यहां मूल समस्या यह है कि आप टाइप ऑब्जेक्ट के मालिक नहीं हैं, और आप नहीं जानते कि कौन इसे एक्सेस कर सकता है। सामान्य तौर पर, यह एक बहुत बुरा विचार है कि आप किसी ऐसी वस्तु को लॉक करने पर भरोसा न करें जो आप नहीं बनाते हैं और यह नहीं जानते कि कौन अन्य तक पहुंच सकता है। ऐसा करना गतिरोध को आमंत्रित करता है। सबसे सुरक्षित तरीका केवल निजी वस्तुओं को लॉक करना है।



2

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

class SomeClass
{
    public void SomeMethod(int id)
    {
        **lock(this)**
        {
            while(true)
            {
                Console.WriteLine("SomeClass.SomeMethod #" + id);
            }
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        SomeClass o = new SomeClass();

        lock(o)
        {
            for (int threadId = 0; threadId < 3; threadId++)
            {
                Thread t = new Thread(() => {
                    o.SomeMethod(threadId);
                        });
                t.Start();
            }

            Console.WriteLine();
        }

चारों ओर काम करने के लिए, इस व्यक्ति ने लॉक के बजाय थ्रेड.ट्रोनमीटर (टाइमआउट के साथ) का उपयोग किया:

            Monitor.TryEnter(temp, millisecondsTimeout, ref lockWasTaken);
            if (lockWasTaken)
            {
                doAction();
            }
            else
            {
                throw new Exception("Could not get lock");
            }

https://blogs.appbeat.io/post/c-how-to-lock-without-deadlocks


जहां तक ​​मैं देखता हूं, जब मैं लॉक (इस) को लॉक करता हूं, तो एक निजी उदाहरण के सदस्य के साथ लॉक होता है SomeClass, फिर भी मुझे वही गतिरोध मिलता है। साथ ही, यदि मुख्य वर्ग में लॉक प्रोग्राम के किसी अन्य निजी उदाहरण सदस्य पर किया जाता है, तो वही लॉक होता है। इसलिए, सुनिश्चित नहीं हैं कि यह उत्तर भ्रामक और गलत नहीं है। : यहाँ में है कि व्यवहार देखें dotnetfiddle.net/DMrU5h
बार्तोज़

1

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

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


1

क्षमा करें दोस्तों, लेकिन मैं इस तर्क से सहमत नहीं हो सकता कि इस पर ताला लगाने से गतिरोध पैदा हो सकता है। आप दो चीजों को भ्रमित कर रहे हैं: गतिरोध और भूखा रहना।

  • आप थ्रेड्स में से एक को बाधित किए बिना डेडलॉक को रद्द नहीं कर सकते हैं ताकि डेडलॉक में आने के बाद आप बाहर न निकल सकें
  • थ्रेड्स में से एक के समाप्त होने के बाद भूखा स्वत: समाप्त हो जाएगा

यहां एक तस्वीर है जो अंतर को दर्शाती है।

निष्कर्ष यदि थ्रेड भुखमरी आपके लिए कोई समस्या नहीं है, तो
आप अभी भी सुरक्षित रूप से उपयोग कर सकते lock(this)हैं। आपको अभी भी यह ध्यान रखना है कि जब थ्रेड, जो धागे का उपयोग करके भूखा lock(this)होता है, एक लॉक में आपकी वस्तु लॉक होती है, तो यह अंततः अनन्त भुखमरी में समाप्त हो जाएगा;)


9
एक अंतर है लेकिन यह पूरी तरह से इस चर्चा के लिए अप्रासंगिक है। और आपके निष्कर्ष का पहला वाक्य सपाट गलत है।
बेन वोइगेट

1
स्पष्ट होने के लिए: मैं बचाव नहीं कर रहा हूं lock(this)- इस तरह का कोड बस गलत है। मुझे लगता है कि इसे गतिरोध कहना थोड़ा अपमानजनक है।
सोयडर

2
छवि से लिंक अब उपलब्ध नहीं है। :( कोई भी मौका आप इसे फिर से संदर्भित कर सकते हैं? Thx
VG1

1

कृपया निम्नलिखित लिंक देखें जो बताता है कि लॉक (यह) एक अच्छा विचार क्यों नहीं है।

http://blogs.msdn.com/b/bclteam/archive/2004/01/20/60719.aspx

इसलिए समाधान एक निजी वस्तु को जोड़ने के लिए है, उदाहरण के लिए, वर्ग को लॉकऑब्जेक्ट करें और नीचे दिखाए गए लॉक स्टेटमेंट के अंदर कोड क्षेत्र रखें:

lock (lockObject)
{
...
}

लिंक अब मान्य नहीं है।
राउलड

1

यहाँ कुछ सैंपल कोड दिए गए हैं जिनका पालन करना सरल है (IMO): ( LinqPad में काम करेगा , नामस्थान के बाद संदर्भ: System.Net और System.Threading.Tasks)

याद रखने वाली बात यह है कि ताला (x) मूल रूप से वाक्यगत चीनी है और यह मॉनिटर का उपयोग करने के लिए क्या करता है। किसी भी चीज़ का उपयोग करें और फिर कोशिश करें, पकड़ें, अंत में मॉनिटर को कॉल करने के लिए ब्लॉक करें। बाहर निकलें देखें: https://docs.microsoft.com/en-us/dotnet/api/system.threading.monitor.enter (टिप्पणी अनुभाग)

या C # लॉक स्टेटमेंट (Visual Basic में SyncLock स्टेटमेंट) का उपयोग करें, जो एक प्रयास में अंत में प्रवेश और निकास विधियों को लपेटता है ... अंत में ब्लॉक।

void Main()
{
    //demonstrates why locking on THIS is BADD! (you should never lock on something that is publicly accessible)
    ClassTest test = new ClassTest();
    lock(test) //locking on the instance of ClassTest
    {
        Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        Parallel.Invoke(new Action[]
        {
            () => {
                //this is there to just use up the current main thread. 
                Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
                },
            //none of these will enter the lock section.
            () => test.DoWorkUsingThisLock(1),//this will dead lock as lock(x) uses Monitor.Enter
            () => test.DoWorkUsingMonitor(2), //this will not dead lock as it uses Montory.TryEnter
        });
    }
}

public class ClassTest
{
    public void DoWorkUsingThisLock(int i)
    {
        Console.WriteLine($"Start ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        lock(this) //this can be bad if someone has locked on this already, as it will cause it to be deadlocked!
        {
            Console.WriteLine($"Running: ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(1000);
        }
        Console.WriteLine($"End ClassTest.DoWorkUsingThisLock Done {i}  CurrentThread {Thread.CurrentThread.ManagedThreadId}");
    }

    public void DoWorkUsingMonitor(int i)
    {
        Console.WriteLine($"Start ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        if (Monitor.TryEnter(this))
        {
            Console.WriteLine($"Running: ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
            Thread.Sleep(1000);
            Monitor.Exit(this);
        }
        else
        {
            Console.WriteLine($"Skipped lock section!  {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        }

        Console.WriteLine($"End ClassTest.DoWorkUsingMonitor Done {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
        Console.WriteLine();
    }
}

उत्पादन

CurrentThread 15
CurrentThread 15
Start ClassTest.DoWorkUsingMonitor 2 CurrentThread 13
Start ClassTest.DoWorkUsingThisLock 1 CurrentThread 12
Skipped lock section!  2 CurrentThread 13
End ClassTest.DoWorkUsingMonitor Done 2 CurrentThread 13

ध्यान दें कि थ्रेड # 12 कभी भी अपने मृत लॉक के रूप में समाप्त नहीं होता है।


1
ऐसा लगता है कि DoWorkUsingThisLockमुद्दे को स्पष्ट करने के लिए दूसरा धागा आवश्यक नहीं है?
जैक लू

आप मुख्य में आउट्टर लॉक का मतलब नहीं है, एक धागा बस दूसरे को पूरा करने के लिए इंतजार करेंगे? जो तब समानांतर को अमान्य कर देगा ... मुझे लगता है कि हमें बेहतर वास्तविक दुनिया के उदाहरणों की आवश्यकता है ..
सीबकिटक

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

0

आप एक नियम स्थापित कर सकते हैं जो कहता है कि एक वर्ग में कोड हो सकता है जो 'इस' या किसी भी ऑब्जेक्ट पर लॉक होता है जो कि क्लास में कोड को इंस्टेंट करता है। तो यह केवल एक समस्या है अगर पैटर्न का पालन नहीं किया जाता है।

यदि आप अपने आप को उस कोड से सुरक्षित रखना चाहते हैं जो इस पैटर्न का पालन नहीं करेगा, तो स्वीकृत उत्तर सही है। लेकिन अगर पैटर्न का पालन किया जाता है, तो यह कोई समस्या नहीं है।

लॉक का लाभ (यह) दक्षता है। क्या होगा यदि आपके पास एक सरल "मूल्य वस्तु" है जो एक मूल्य रखता है। यह सिर्फ एक आवरण है, और यह लाखों बार तात्कालिक हो जाता है। केवल लॉकिंग के लिए एक निजी सिंक ऑब्जेक्ट के निर्माण की आवश्यकता होने से, आपने मूल रूप से ऑब्जेक्ट का आकार दोगुना कर दिया है और आवंटन की संख्या दोगुनी कर दी है। जब प्रदर्शन मायने रखता है, तो यह एक फायदा है।

जब आप आवंटन या मेमोरी फ़ुटप्रिंट की संख्या की परवाह नहीं करते हैं, तो लॉक से बचना (यह) अन्य उत्तरों में संकेतित कारणों के लिए बेहतर है।


-1

यदि सार्वजनिक रूप से उदाहरण तक पहुँचा जा सकता है तो एक समस्या होगी क्योंकि अन्य अनुरोध हो सकते हैं जो समान ऑब्जेक्ट उदाहरण का उपयोग कर रहे हों। निजी / स्थिर चर का उपयोग करना बेहतर है।


5
यह सुनिश्चित नहीं है कि आदमी को क्या जोड़ता है, पहले से मौजूद विस्तृत जवाब जो एक ही बात कहते हैं।
एंड्रयू बार्बर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.