C# Language
लॉक स्टेटमेंट
खोज…
वाक्य - विन्यास
- ताला (obj) {}
टिप्पणियों
lock
स्टेटमेंट का उपयोग करके आप कोड के भीतर विभिन्न थ्रेड्स की पहुंच को नियंत्रित कर सकते हैं। यह आमतौर पर दौड़ की स्थिति को रोकने के लिए उपयोग किया जाता है, उदाहरण के लिए एक संग्रह से कई थ्रेड्स पढ़ना और आइटम हटाना। लॉकिंग फोर्स थ्रेड्स को अन्य थ्रेड्स के लिए प्रतीक्षा करने के लिए एक कोड ब्लॉक से बाहर निकलने के लिए यह देरी का कारण बन सकता है जिसे अन्य सिंक्रनाइज़ेशन विधियों के साथ हल किया जा सकता है।
MSDN
लॉक कीवर्ड स्टेटमेंट ब्लॉक को दिए गए ऑब्जेक्ट के लिए म्यूचुअल-एक्सक्लूजन लॉक प्राप्त करके, स्टेटमेंट निष्पादित करते हुए और फिर लॉक को रिलीज़ करके एक महत्वपूर्ण खंड के रूप में चिह्नित करता है।
लॉक कीवर्ड यह सुनिश्चित करता है कि एक धागा कोड के एक महत्वपूर्ण खंड में प्रवेश नहीं करता है, जबकि दूसरा धागा महत्वपूर्ण अनुभाग में है। यदि कोई अन्य थ्रेड किसी बंद कोड को दर्ज करने का प्रयास करता है, तो वह तब तक प्रतीक्षा करेगा, जब तक कि ऑब्जेक्ट जारी न हो जाए।
सबसे अच्छा अभ्यास सभी उदाहरणों के लिए डेटा की रक्षा के लिए एक निजी ऑब्जेक्ट को लॉक करने के लिए या एक निजी स्थिर ऑब्जेक्ट चर को परिभाषित करना है।
C # 5.0 और बाद में, lock
स्टेटमेंट इसके बराबर है:
bool lockTaken = false;
try
{
System.Threading.Monitor.Enter(refObject, ref lockTaken);
// code
}
finally
{
if (lockTaken)
System.Threading.Monitor.Exit(refObject);
}
C # 4.0 और इससे पहले के लिए, lock
स्टेटमेंट इसके बराबर है:
System.Threading.Monitor.Enter(refObject);
try
{
// code
}
finally
{
System.Threading.Monitor.Exit(refObject);
}
सरल उपयोग
lock
का सामान्य उपयोग एक महत्वपूर्ण खंड है।
निम्नलिखित उदाहरण में ReserveRoom
को विभिन्न थ्रेड्स से कहा जाता है। यहां रेस की स्थिति को रोकने के लिए lock
साथ सिंक्रोनाइज़ेशन सबसे सरल तरीका है। विधि निकाय lock
से घिरा हुआ है जो यह सुनिश्चित करता है कि दो या अधिक धागे इसे एक साथ निष्पादित नहीं कर सकते हैं।
public class Hotel
{
private readonly object _roomLock = new object();
public void ReserveRoom(int roomNumber)
{
// lock keyword ensures that only one thread executes critical section at once
// in this case, reserves a hotel room of given number
// preventing double bookings
lock (_roomLock)
{
// reserve room logic goes here
}
}
}
यदि एक थ्रेड lock
ब्लॉक तक पहुंच जाता है, जबकि उसके भीतर एक और थ्रेड चल रहा है, तो पूर्व ब्लॉक से बाहर निकलने के लिए एक और प्रतीक्षा करेगा।
सबसे अच्छा अभ्यास सभी उदाहरणों के लिए डेटा की रक्षा के लिए एक निजी ऑब्जेक्ट को लॉक करने के लिए या एक निजी स्थिर ऑब्जेक्ट चर को परिभाषित करना है।
एक लॉक स्टेटमेंट में अपवाद को फेंकना
निम्नलिखित कोड लॉक जारी करेगा। कोई दिक्कत नहीं होगी। पर्दे के पीछे का बयान try finally
रूप में काम करता है
lock(locker)
{
throw new Exception();
}
C # 5.0 विशिष्टता में अधिक देखा जा सकता है:
फॉर्म का एक lock
स्टेटमेंट
lock (x) ...
जहाँ x
एक संदर्भ-प्रकार की अभिव्यक्ति है, ठीक इसके बराबर है
bool __lockWasTaken = false;
try {
System.Threading.Monitor.Enter(x, ref __lockWasTaken);
...
}
finally {
if (__lockWasTaken) System.Threading.Monitor.Exit(x);
}
सिवाय इसके कि x
का केवल एक बार मूल्यांकन किया जाए।
एक लॉक स्टेटमेंट में लौटें
निम्नलिखित कोड लॉक जारी करेगा।
lock(locker)
{
return 5;
}
एक विस्तृत विवरण के लिए, यह SO उत्तर अनुशंसित है।
लॉक के लिए ऑब्जेक्ट के उदाहरणों का उपयोग करना
जब C # के इनबिल्ट lock
स्टेटमेंट का उपयोग किया जाता है, तो किसी प्रकार की आवृत्ति की आवश्यकता होती है, लेकिन इसकी स्थिति कोई मायने नहीं रखती है। object
का एक उदाहरण इसके लिए एकदम सही है:
public class ThreadSafe {
private static readonly object locker = new object();
public void SomeThreadSafeMethod() {
lock (locker) {
// Only one thread can be here at a time.
}
}
}
एनबी । इसके लिए Type
उदाहरणों का उपयोग नहीं किया जाना चाहिए typeof(ThreadSafe)
के ऊपर कोड में typeof(ThreadSafe)
) क्योंकि Type
उदाहरणों को typeof(ThreadSafe)
में साझा किया जाता है और इस प्रकार लॉक की सीमा में अपेक्षित रूप से कोड शामिल नहीं किया जा सकता है (जैसे। यदि ThreadSafe
को लोड किया गया है। एक ही प्रक्रिया में दो AppDomains फिर इसके Type
उदाहरण पर लॉक करना पारस्परिक रूप से लॉक होगा)।
विरोधी पैटर्न और gotchas
स्टैक-आवंटित / स्थानीय चर पर लॉक करना
lock
का उपयोग करते समय होने वाली गिरावट में से एक फ़ंक्शन में लॉकर के रूप में स्थानीय वस्तुओं का उपयोग होता है। चूंकि ये स्थानीय ऑब्जेक्ट इंस्टेंसेस फ़ंक्शन के प्रत्येक कॉल पर भिन्न होंगे, इसलिए lock
अपेक्षित रूप से प्रदर्शन नहीं करेगा।
List<string> stringList = new List<string>();
public void AddToListNotThreadSafe(string something)
{
// DO NOT do this, as each call to this method
// will lock on a different instance of an Object.
// This provides no thread safety, it only degrades performance.
var localLock = new Object();
lock(localLock)
{
stringList.Add(something);
}
}
// Define object that can be used for thread safety in the AddToList method
readonly object classLock = new object();
public void AddToList(List<string> stringList, string something)
{
// USE THE classLock instance field to achieve a
// thread-safe lock before adding to stringList
lock(classLock)
{
stringList.Add(something);
}
}
यह मानते हुए कि लॉकिंग सिंक्रनाइज़ेशन ऑब्जेक्ट तक ही पहुंच को प्रतिबंधित करता है
यदि एक थ्रेड कॉल करता है: lock(obj)
और दूसरा थ्रेड obj.ToString()
दूसरा थ्रेड ब्लॉक होने वाला नहीं है।
object obj = new Object();
public void SomeMethod()
{
lock(obj)
{
//do dangerous stuff
}
}
//Meanwhile on other tread
public void SomeOtherMethod()
{
var objInString = obj.ToString(); //this does not block
}
लॉक करने के लिए पता करने के लिए उपवर्गों की अपेक्षा करना
कभी-कभी आधार वर्ग ऐसे डिज़ाइन किए जाते हैं कि उनके उप-वर्गों को कुछ संरक्षित क्षेत्रों तक पहुँचने के लिए लॉक का उपयोग करना आवश्यक होता है:
public abstract class Base
{
protected readonly object padlock;
protected readonly List<string> list;
public Base()
{
this.padlock = new object();
this.list = new List<string>();
}
public abstract void Do();
}
public class Derived1 : Base
{
public override void Do()
{
lock (this.padlock)
{
this.list.Add("Derived1");
}
}
}
public class Derived2 : Base
{
public override void Do()
{
this.list.Add("Derived2"); // OOPS! I forgot to lock!
}
}
टेम्प्लेट विधि का उपयोग करके लॉकिंग को एनकैप्सुलेट करना अधिक सुरक्षित है:
public abstract class Base
{
private readonly object padlock; // This is now private
protected readonly List<string> list;
public Base()
{
this.padlock = new object();
this.list = new List<string>();
}
public void Do()
{
lock (this.padlock) {
this.DoInternal();
}
}
protected abstract void DoInternal();
}
public class Derived1 : Base
{
protected override void DoInternal()
{
this.list.Add("Derived1"); // Yay! No need to lock
}
}
एक बॉक्सिंग वैल्यू टाइप वेरिएबल पर लॉक करने से सिंक्रोनाइज़ नहीं होता है
निम्नलिखित उदाहरण में, एक निजी चर को स्पष्ट रूप से बॉक्सिंग किया जाता है क्योंकि यह एक फ़ंक्शन के object
तर्क के रूप में दिया जाता है, मॉनिटर संसाधन को लॉक करने की अपेक्षा करता है। IncInSync फ़ंक्शन को कॉल करने से ठीक पहले बॉक्सिंग होती है, इसलिए प्रत्येक बार फ़ंक्शन को कॉल करने पर बॉक्सिंग आवृत्ति एक अलग हीप ऑब्जेक्ट से मेल खाती है।
public int Count { get; private set; }
private readonly int counterLock = 1;
public void Inc()
{
IncInSync(counterLock);
}
private void IncInSync(object monitorResource)
{
lock (monitorResource)
{
Count++;
}
}
बॉक्सिंग Inc
फंक्शन में होता है:
BulemicCounter.Inc:
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldfld UserQuery+BulemicCounter.counterLock
IL_0008: box System.Int32**
IL_000D: call UserQuery+BulemicCounter.IncInSync
IL_0012: nop
IL_0013: ret
इसका मतलब यह नहीं है कि एक बॉक्सिंग वैल्यू टाइप का उपयोग मॉनिटर लॉकिंग के लिए बिल्कुल भी नहीं किया जा सकता है:
private readonly object counterLock = 1;
अब बॉक्सिंग कंस्ट्रक्टर में होता है, जो लॉकिंग के लिए ठीक है:
IL_0001: ldc.i4.1
IL_0002: box System.Int32
IL_0007: stfld UserQuery+BulemicCounter.counterLock
सुरक्षित विकल्प मौजूद होने पर अनावश्यक रूप से ताले का उपयोग करना
एक बहुत ही सामान्य पैटर्न एक निजी List
या Dictionary
का उपयोग थ्रेड सेफ क्लास में करना और हर बार इसे एक्सेस करने के लिए लॉक करना है:
public class Cache
{
private readonly object padlock;
private readonly Dictionary<string, object> values;
public WordStats()
{
this.padlock = new object();
this.values = new Dictionary<string, object>();
}
public void Add(string key, object value)
{
lock (this.padlock)
{
this.values.Add(key, value);
}
}
/* rest of class omitted */
}
यदि values
शब्दकोश तक पहुँचने के लिए कई तरीके हैं, तो कोड बहुत लंबा हो सकता है और इससे भी महत्वपूर्ण बात यह है कि हर समय उसके इरादे को अस्पष्ट करता है। लॉक करना भी भूलना बहुत आसान है और उचित लॉकिंग की कमी से बग्स को खोजने में बहुत मुश्किल हो सकती है।
एक ConcurrentDictionary
का उपयोग करके, हम पूरी तरह से लॉक करने से बच सकते हैं:
public class Cache
{
private readonly ConcurrentDictionary<string, object> values;
public WordStats()
{
this.values = new ConcurrentDictionary<string, object>();
}
public void Add(string key, object value)
{
this.values.Add(key, value);
}
/* rest of class omitted */
}
समवर्ती संग्रहों का उपयोग करने से प्रदर्शन में भी सुधार होता है क्योंकि ये सभी कुछ हद तक लॉक-फ्री तकनीकों को नियोजित करते हैं ।