2011-11-25 18 views
42

मैं lock करने के लिए एक Boolean चर कोशिश कर रहा था जब मैं निम्न त्रुटि का सामना करना पड़ा:हम मूल्य प्रकार को क्यों लॉक नहीं कर सकते?

'bool' एक संदर्भ प्रकार के रूप में ताला बयान के लिए आवश्यक नहीं है

ऐसा लगता है कि केवल संदर्भ के प्रकार के होते हैं lock बयान में अनुमति है, लेकिन मुझे यकीन नहीं है कि मैं क्यों समझता हूं।

एंड्रियास उसकी comment में बताते हुए है:

जब [एक मान प्रकार] वस्तु एक धागे से दूसरे से पारित कर दिया है, एक प्रतिलिपि बना रहा है, इसलिए धागे 2 विभिन्न वस्तुओं, पर काम कर रहा है जो अंत सुरक्षित है।

क्या यह सच है? क्या इसका मतलब यह है कि जब मैं निम्नलिखित करता हूं, तो मैं वास्तव में xToTrue और xToFalse विधि में दो अलग-अलग x संशोधित कर रहा हूं?

public static class Program { 

    public static Boolean x = false; 

    [STAThread] 
    static void Main(string[] args) { 

     var t = new Thread(() => xToTrue()); 
     t.Start(); 
     // ... 
     xToFalse(); 
    } 

    private static void xToTrue() { 
     Program.x = true; 
    } 

    private static void xToFalse() { 
     Program.x = false; 
    } 
} 

(अकेले इस कोड अपने राज्य में स्पष्ट रूप से बेकार है, यह केवल उदाहरण के लिए)


पी.एस: मैं How to properly lock a value type पर इस सवाल के बारे में पता है। मेरा प्रश्न से संबंधित नहीं है लेकिन क्यों पर।

+0

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

+0

@ मार्टिनब्राउन: मुझे पता है, और मेरे * असली * कोड में, मैं एक समर्पित ऑब्जेक्ट पर लॉक का उपयोग करता हूं (जैसा कि मैंने अपने प्रश्न में उल्लेख किया है, यह * क्यों * और * कैसे * से संबंधित है)। 'अस्थिर' के लिए, यह [ठीक से लॉक करते समय आवश्यक नहीं है] (http://blogs.msdn.com/b/ericlippert/archive/2011/06/16/atomicity-volatility-and-immutability-are- अलग- अंशकालिक three.aspx)। – Otiel

उत्तर

32

बस एक जंगली अनुमान यहाँ ...

लेकिन अगर संकलक आप एक मूल्य के प्रकार पर ताला करते हैं, आप कुछ भी नहीं ताला लगा ... अंत होगा क्योंकि हर बार जब आप lock को मान प्रकार पारित कर दिया , आप इसकी एक बॉक्स की प्रतिलिपि पारित करेंगे; एक अलग बॉक्स की प्रतिलिपि। तो ताले ऐसा होगा जैसे वे पूरी तरह से अलग वस्तुओं थे। (चूंकि, वे वास्तव में हैं)

याद रखें कि जब आप object प्रकार के पैरामीटर के लिए मान प्रकार पास करते हैं, तो यह संदर्भ प्रकार में बॉक्स किए गए (लपेटा) हो जाता है। यह हर बार ऐसा होता है जब यह एक नई वस्तु बनाता है।

+3

मुख्य बिंदु यह है कि मूल्य प्रकार प्रत्येक बार एक अलग वस्तु में बॉक्स किया जाएगा (मुक्केबाजी प्रतिलिपि के समान नहीं है और मुझे लगता है कि ओपी और भविष्य के पाठकों के लिए यह ध्यान देने योग्य है)। [मेरा जवाब] देखें (http://stackoverflow.com/q/8267344/593627) –

+0

यह एक अच्छा बिंदु है। मैं संपादित करूंगा। –

+0

संकलक मूल्य प्रकार लॉक करते समय एक अदृश्य निश्चित संदर्भ प्रकार बना सकता है। – drowa

15

यह फैलता करने के लिए:

System.Threading.Monitor.Enter(x); 
try { 
    ... 
} 
finally { 
    System.Threading.Monitor.Exit(x); 
} 

हालांकि वे संकलन होगा, Monitor.Enter/Exit एक संदर्भ प्रकार की आवश्यकता होती है क्योंकि एक मान प्रकार एक अलग वस्तु उदाहरण के लिए हर बार बॉक्सिंग जाएगा ताकि Enter और Exit की प्रत्येक कॉल किया जाएगा विभिन्न वस्तुओं पर परिचालन।

MSDN Enter method पृष्ठ से:

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

+1

और क्यों 'मॉनीटर.इंटर' और 'मॉनीटर.एक्सिट' को संदर्भ प्रकार की आवश्यकता है? (पूछने के लिए स्पष्ट सवाल की तरह लगता है, क्योंकि यह वास्तव में ओपी के बाद क्या है)। – Oded

+0

क्षमा करें, बस संपादित किया गया है –

+0

जब संकलक द्वारा चेक किया गया नहीं है तो यह नहीं है ... इसे संदर्भ प्रकार के लिए उचित प्रकार की आवश्यकता हो सकती है, और यदि कोई बॉक्स बॉक्स मूल्य मान है तो यह अपवाद भी फेंक सकता है जाँच की। लेकिन यह सिर्फ ठीक है। कंपाइलर द्वारा किए गए संदर्भ प्रकार की जांच केवल लॉक() कथन के लिए है। –

2

क्योंकि मान प्रकारों में सिंक ब्लॉक नहीं है जो किसी ऑब्जेक्ट को लॉक करने के लिए लॉक स्टेटमेंट का उपयोग करता है। केवल संदर्भ प्रकारों में टाइप जानकारी, सिंक ब्लॉक इत्यादि का ओवरहेड होता है

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

यह शायद जब तक काम करेगा (हालांकि यह पूरी तरह से व्यर्थ है और मैं इसे करने की कोशिश नहीं की है)

int x = 7; 
object boxed = (object)x; 

//thread1: 
lock (boxed){ 
... 
} 
//thread2: 
lock(boxed){ 
... 
} 

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

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

+0

मेरे दूसरे प्रश्न का उत्तर देने के लिए धन्यवाद। यह मेरे लिए अजीब लग रहा था कि प्रत्येक धागे के लिए मूल्यों की प्रतिलिपि बनाई जाती है। – Otiel

1

निम्नलिखित MSDN से लिया जाता है:

ताला (सी #) और SyncLock (विजुअल बेसिक) बयान सुनिश्चित करना है कि कोड का एक खंड के बिना अन्य थ्रेड द्वारा रुकावट पूरा होने से चलाता है इस्तेमाल किया जा सकता। यह कोड ब्लॉक की अवधि के लिए किसी दिए गए ऑब्जेक्ट के लिए आपसी-बहिष्करण लॉक प्राप्त करके पूरा किया जाता है।

और

ताला कीवर्ड के लिए प्रदान की तर्क एक वस्तु एक संदर्भ प्रकार के आधार पर होना चाहिए, और ताला के क्षेत्र को परिभाषित करने के लिए प्रयोग किया जाता है।

मुझे लगता है कि यह एक भाग में है क्योंकि लॉक तंत्र आपसी बहिष्करण लॉक बनाने के लिए उस ऑब्जेक्ट का एक उदाहरण उपयोग करता है।

4

आप यह क्यों की अनुमति नहीं है, मैं कहूंगा कि इस सवाल का जवाब तथ्य यह है कि एक मान प्रकार के पहचान वास्तव में अपने मूल्य (है कि यह एक मूल्य बना देता है क्या के बराबर है की वजह से उपजी धारणात्मक पूछ रहे हैं प्रकार)।

तो ब्रह्मांड int4 के बारे में बात में कहीं भी किसी को भी बात कर रही है के बारे में एक ही बात - कैसे तो आप संभवतः उस पर ताला के लिए अनन्य पहुँच दावा कर सकते हैं?

+0

यह एक दिलचस्प बात है; विशेष रूप से यदि कोई पूछता है, "इसलिए जब किसी ऑब्जेक्ट परम पर पास होने पर एक मान प्रकार बॉक्स किया जाता है, तो लॉक को अतिरिक्त मूल्य प्रकारों को भी क्यों स्वीकार नहीं किया जाता है?" ... क्योंकि ऐसा करने के लिए, लॉक मूल्य पर होगा - नहीं परिवर्तनीय वह बेकार होगा। +1 –

1

इस MSDN Thread के अनुसार, संदर्भ चर में परिवर्तन सभी धागे के लिए दृश्यमान नहीं हो सकते हैं और वे पुराने मूल्यों का उपयोग कर समाप्त हो सकते हैं, और AFAIK मुझे लगता है कि मान प्रकार धागे के बीच पारित होने पर प्रतिलिपि बनाते हैं।

MSDN

से बिल्कुल के शब्दों में

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

+3

विभिन्न धागे द्वारा उपयोग किए जाने पर मान प्रकार की प्रतिलिपि नहीं बनाई जाती है। यह यादगार में एक ही पता है - कारण आपको पुरानी मान मिल सकती है कि आपके द्वारा एक थ्रेड में सेट किया गया मान तुरंत स्मृति स्थान पर वापस नहीं लिखा जा सकता है - यह एक रजिस्टर में 'कैश किया जा सकता है' क्योंकि जेआईटी इसे देख सकता है जल्द ही इस्तेमाल किया जा रहा है। –

+0

धन्यवाद, मुझे नहीं पता, मुझे SO – Vamsi

0

मुझे लगता है कि यह उन मामलों में से एक है जहां इसका जवाब है "क्योंकि एक माइक्रोसॉफ्ट इंजीनियर ने इसे इस तरह कार्यान्वित किया"।

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

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

+2

पसंद है मेरा मानना ​​है कि माइक्रोसॉफ्ट ने इस तरह से काम किया था कि एक तत्काल लॉक प्रकार या तो होगा: (1) एक फाइनलाइज़र की आवश्यकता होती है, (2) लीक संसाधनों में प्रवेश किया जाता है लेकिन बाएं नहीं , या (3) छोड़े जाने पर सफाई के लिए कुछ अन्य जीसी समर्थन की आवश्यकता है। मेरा अनुमान है कि एमएस ने दृष्टिकोण # 3 पर फैसला किया है, और इसे इस तरह कार्यान्वित किया है कि प्रत्येक वर्ग वस्तु का मूल्य समान रूप से लगाया जाएगा, और इस प्रकार प्रत्येक वर्ग वस्तु पर लॉकिंग की अनुमति देने के लिए कोई तकनीकी बाधा नहीं थी। – supercat

25

आप एक मान प्रकार को लॉक नहीं कर सकते क्योंकि इसमें sync root रिकॉर्ड नहीं है।

लॉकिंग सीएलआर और ओएस आंतरिक तंत्र द्वारा किया जाता है जो एक ऑब्जेक्ट पर निर्भर करता है जिसमें एक रिकॉर्ड होता है जिसे केवल एक ही थ्रेड द्वारा सिंक ब्लॉक रूट तक पहुंचा जा सकता है। कोई संदर्भ प्रकार होगा:

  • सूचक एक विशेष प्रकार के
  • सिंक ब्लॉक जड़
  • सूचक उदाहरण के लिए ढेर में डेटा
4

मैं सोच रहा था क्यों नेट टीम डेवलपर्स को सीमित करने का निर्णय लिया और मॉनिटर को संदर्भों पर ही काम करने की अनुमति दें। सबसे पहले, आपको लगता है कि लॉकिंग उद्देश्य के लिए समर्पित ऑब्जेक्ट वैरिएबल को परिभाषित करने के बजाय System.Int32 के विरुद्ध लॉक करना अच्छा होगा, ये लॉकर आमतौर पर कुछ भी नहीं करते हैं।

लेकिन फिर ऐसा लगता है कि भाषा द्वारा प्रदान की गई किसी भी सुविधा में मजबूत अर्थशास्त्र होना चाहिए न केवल डेवलपर्स के लिए उपयोगी होना चाहिए। तो मूल्य-प्रकार वाले अर्थशास्त्र यह है कि जब भी कोड में मूल्य-प्रकार प्रकट होता है तो इसकी अभिव्यक्ति का मूल्यांकन मूल्य पर किया जाता है। तो, अर्थात् दृष्टिकोण से, यदि हम 'लॉक (एक्स)' लिखते हैं और एक्स एक आदिम मूल्य प्रकार है तो यह वही है जैसा कि हम कहेंगे "महत्वपूर्ण कोड agaist के एक ब्लॉक को वैरिएबल एक्स के मान को लॉक करें" जो अधिक लगता है अजीब से, निश्चित रूप से :)। इस बीच, जब हम कोड में रेफ वैरिएबल को पूरा करते हैं तो हम सोचते हैं कि "ओह, यह किसी ऑब्जेक्ट का संदर्भ है" और यह दर्शाता है कि संदर्भ कोड ब्लॉक, विधियों, कक्षाओं और यहां तक ​​कि धागे और प्रक्रियाओं के बीच साझा किया जा सकता है और इस प्रकार एक के रूप में कार्य कर सकता है रक्षक।

दो शब्दों में, मूल्य प्रकार चर केवल कोड में दिखाई देते हैं ताकि प्रत्येक अभिव्यक्ति में उनके वास्तविक मूल्य का मूल्यांकन किया जा सके - और कुछ नहीं।

मुझे लगता है कि यह मुख्य बिंदुओं में से एक है।

+2

+1 बस समझाया गया है, और आपने @ मार्टिन ब्राउन के साथ-साथ जवाब दिया है – dotnetguy

संबंधित मुद्दे