क्या कोई C # में अस्थिर कीवर्ड का अच्छा विवरण दे सकता है? यह कौन सी समस्याओं को हल करता है और कौन सा नहीं है? किन मामलों में यह मुझे लॉकिंग के उपयोग से बचाएगा?
क्या कोई C # में अस्थिर कीवर्ड का अच्छा विवरण दे सकता है? यह कौन सी समस्याओं को हल करता है और कौन सा नहीं है? किन मामलों में यह मुझे लॉकिंग के उपयोग से बचाएगा?
जवाबों:
मुझे नहीं लगता कि एरिक लिपर्ट (मूल में जोर) की तुलना में इसका जवाब देने के लिए एक बेहतर व्यक्ति है :
C # में, "वाष्पशील" का अर्थ केवल यह नहीं है कि "सुनिश्चित करें कि कंपाइलर और जिटर इस चर पर कैशिंग ऑप्टिमाइज़ेशन का कोई कोड पुन: व्यवस्थित या पंजीकृत नहीं करते हैं"। इसका मतलब यह भी है कि "प्रोसेसर को यह बताने के लिए कि उसे जो कुछ भी करना है, उसे यह सुनिश्चित करने के लिए कि मुझे नवीनतम मूल्य पढ़ना है, भले ही इसका मतलब है कि अन्य प्रोसेसर को रोकना और उन्हें अपने कैश के साथ मुख्य मेमोरी को सिंक्रनाइज़ करना है"।
दरअसल, वह आखिरी सा झूठ है। वाष्पशील का सच्चा शब्दार्थ पढ़ता है और लिखता है कि मैं यहाँ उल्लिखित की तुलना में बहुत अधिक जटिल हूँ; वास्तव में वे वास्तव में यह गारंटी नहीं देते हैं कि प्रत्येक प्रोसेसर बंद हो जाता है कि वह क्या कर रहा है और मुख्य मेमोरी से / के लिए कैश अपडेट करता है। इसके बजाय, वे इस बारे में कमजोर गारंटी प्रदान करते हैं कि पढ़ने और लिखने से पहले मेमोरी कैसे एक्सेस करती है और एक दूसरे के संबंध में आदेश के साथ देखा जा सकता है । कुछ संचालन जैसे कि एक नया धागा बनाना, एक लॉक में प्रवेश करना, या विधियों में से किसी एक इंटरलॉक परिवार का उपयोग करके ऑर्डर के अवलोकन के बारे में मजबूत गारंटी देता है। यदि आप अधिक विवरण चाहते हैं, तो C # 4.0 विनिर्देशन के खंड 3.10 और 10.5.3 पढ़ें।
सच कहूँ, मैं तुम्हें एक अस्थिर क्षेत्र बनाने से हतोत्साहित करता हूँ । वाष्पशील क्षेत्र एक संकेत है कि आप कुछ पागल कर रहे हैं: आप एक ही स्थान पर ताला लगाए बिना दो अलग-अलग थ्रेड्स पर एक ही मूल्य को पढ़ने और लिखने का प्रयास कर रहे हैं। ताले गारंटी देते हैं कि लॉक के अंदर पढ़ी गई या संशोधित की गई मेमोरी सुसंगत मानी जाती है, ताले गारंटी देते हैं कि एक बार में केवल एक थ्रेड किसी दिए गए मेमोरी को एक्सेस करता है, और इसी तरह। उन स्थितियों की संख्या जिसमें एक लॉक बहुत धीमा है, और संभावना है कि आप कोड गलत प्राप्त करने जा रहे हैं क्योंकि आपको सटीक मेमोरी मॉडल समझ में नहीं आता है। मैं इंटरलॉक किए गए कार्यों के सबसे तुच्छ उपयोगों को छोड़कर किसी भी निम्न-लॉक कोड को लिखने का प्रयास नहीं करता हूं। मैं वास्तविक विशेषज्ञों के लिए "अस्थिर" का उपयोग छोड़ देता हूं।
आगे पढ़ने के लिए देखें:
volatile
लॉक के आधार पर होंगी
यदि आप इस बारे में थोड़ा और अधिक तकनीकी प्राप्त करना चाहते हैं कि वाष्पशील कीवर्ड क्या करता है, तो निम्नलिखित कार्यक्रम पर विचार करें (मैं DevStudio 2005 का उपयोग कर रहा हूं):
#include <iostream>
void main()
{
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
j += i;
}
for (volatile int i = 0 ; i < 100 ; ++i)
{
j += i;
}
std::cout << j;
}
मानक अनुकूलित (रिलीज़) संकलक सेटिंग्स का उपयोग करके, कंपाइलर निम्नलिखित कोडांतरक (IA32) बनाता है:
void main()
{
00401000 push ecx
int j = 0;
00401001 xor ecx,ecx
for (int i = 0 ; i < 100 ; ++i)
00401003 xor eax,eax
00401005 mov edx,1
0040100A lea ebx,[ebx]
{
j += i;
00401010 add ecx,eax
00401012 add eax,edx
00401014 cmp eax,64h
00401017 jl main+10h (401010h)
}
for (volatile int i = 0 ; i < 100 ; ++i)
00401019 mov dword ptr [esp],0
00401020 mov eax,dword ptr [esp]
00401023 cmp eax,64h
00401026 jge main+3Eh (40103Eh)
00401028 jmp main+30h (401030h)
0040102A lea ebx,[ebx]
{
j += i;
00401030 add ecx,dword ptr [esp]
00401033 add dword ptr [esp],edx
00401036 mov eax,dword ptr [esp]
00401039 cmp eax,64h
0040103C jl main+30h (401030h)
}
std::cout << j;
0040103E push ecx
0040103F mov ecx,dword ptr [__imp_std::cout (40203Ch)]
00401045 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (402038h)]
}
0040104B xor eax,eax
0040104D pop ecx
0040104E ret
आउटपुट को देखते हुए, कंपाइलर ने j वैरिएबल के मान को स्टोर करने के लिए ecx रजिस्टर का उपयोग करने का निर्णय लिया है। गैर-वाष्पशील लूप (पहले) के लिए संकलक ने मुझे ईएक्सएक्स रजिस्टर में सौंपा है। काफी सरल। हालांकि दिलचस्प बिट्स के एक जोड़े हैं - लीक ईबेक्स, [ईबेक्स] निर्देश प्रभावी रूप से एक मल्टीबाइट एनओपी निर्देश है ताकि लूप 16 बाइट संरेखित मेमोरी पते पर कूदता है। अन्य ईडीएक्स अनुदेश का उपयोग करने के बजाय लूप काउंटर को बढ़ाने के लिए edx का उपयोग है। ऐड reg, reg इंस्ट्रक्शंस में inc reg निर्देश की तुलना में कुछ IA32 कोर पर कम विलंबता है, लेकिन उच्च विलंबता कभी नहीं है।
अब वाष्पशील लूप काउंटर के साथ लूप के लिए। काउंटर को [esp] पर संग्रहित किया जाता है और वाष्पशील कीवर्ड संकलक को बताता है कि मूल्य को हमेशा मेमोरी से लिखा / पढ़ा जाना चाहिए और कभी भी एक रजिस्टर को नहीं सौंपा जाना चाहिए। कंपाइलर यहां तक जाता है कि तीन अलग-अलग चरणों के रूप में एक लोड / इंक्रीमेंट / स्टोर न करें (काउंटर ईआरसी लोड करते समय ईआरएक्स, इंक ईएक्सएक्स, ईआरएक्स सहेजें), इसके बजाय मेमोरी को एक निर्देश में सीधे संशोधित किया जाता है (एक ऐड मेम , reg)। जिस तरह से कोड बनाया गया है, यह सुनिश्चित करता है कि लूप काउंटर का मूल्य एकल सीपीयू कोर के संदर्भ में हमेशा अद्यतित रहे। डेटा पर कोई भी संचालन भ्रष्टाचार या डेटा हानि का परिणाम नहीं हो सकता है (इसलिए लोड / इंक / स्टोर का उपयोग नहीं करना क्योंकि मूल्य इस प्रकार स्टोर में खो जाने के दौरान बदल सकता है)। चूंकि वर्तमान निर्देश पूरा हो जाने के बाद से व्यवधानों को केवल सेवित किया जा सकता है,
एक बार जब आप सिस्टम में एक दूसरा सीपीयू पेश करते हैं, तो एक ही समय में दूसरे सीपीयू द्वारा अपडेट किए जा रहे डेटा से वाष्पशील कीवर्ड की रक्षा नहीं होगी। उपरोक्त उदाहरण में, संभावित भ्रष्टाचार को प्राप्त करने के लिए आपको डेटा को अविभाजित करने की आवश्यकता होगी। अस्थिर कीवर्ड संभावित भ्रष्टाचार को नहीं रोक पाएगा, यदि डेटा को परमाणु रूप से नियंत्रित नहीं किया जा सकता है, उदाहरण के लिए, यदि लूप काउंटर लंबे समय तक (64 बिट्स) का था, तो मूल्य को अपडेट करने के लिए दो 32 बिट संचालन की आवश्यकता होगी, बीच में जो एक बाधा हो सकती है और डेटा को बदल सकती है।
इसलिए, वाष्पशील कीवर्ड केवल संरेखित डेटा के लिए अच्छा है जो कि मूल रजिस्टरों के आकार से कम या बराबर है, जैसे कि ऑपरेशन हमेशा परमाणु होते हैं।
अस्थिर कीवर्ड की कल्पना आईओ ऑपरेशंस के साथ की जाती थी, जहां आईओ लगातार बदल रहा होगा, लेकिन एक निरंतर पता था, जैसे कि मेमोरी मैप्ड यूएआर डिवाइस, और कंपाइलर को पता से पढ़े गए पहले मान का पुन: उपयोग नहीं करना चाहिए।
यदि आप बड़े डेटा को संभाल रहे हैं या आपके पास कई सीपीयू हैं तो आपको डेटा एक्सेस को ठीक से संभालने के लिए उच्च स्तर (ओएस) लॉकिंग सिस्टम की आवश्यकता होगी।
यदि आप .NET 1.1 का उपयोग कर रहे हैं, तो डबल चेक लॉकिंग करते समय अस्थिर कीवर्ड की आवश्यकता होती है। क्यों? .NET 2.0 से पहले, निम्न परिदृश्य एक गैर-नल तक पहुँचने के लिए दूसरा धागा पैदा कर सकता है, फिर भी पूरी तरह से निर्मित वस्तु नहीं:
.NET 2.0 से पहले, इस। को फू का नया उदाहरण सौंपा जा सकता है, इससे पहले कि कंस्ट्रक्टर समाप्त हो रहा था। इस स्थिति में, एक दूसरा धागा आ सकता है (थू के निर्माता को थ्रेड 1 के कॉल के दौरान) और निम्नलिखित का अनुभव कर सकते हैं:
.NET 2.0 से पहले, आप इस समस्या के बारे में जानने के लिए इसे अस्थिर होने की घोषणा कर सकते हैं। .NET 2.0 के बाद से, आपको अब डबल चेक किए गए लॉकिंग को पूरा करने के लिए अस्थिर कीवर्ड का उपयोग करने की आवश्यकता नहीं है।
विकिपीडिया वास्तव में डबल चेक्ड लॉकिंग पर एक अच्छा लेख है, और इस विषय पर संक्षिप्त रूप से स्पर्श करता है: http://en.wikipedia.org/wiki/Double-checked_locking
foo
? क्या थ्रेड 1 लॉकिंग नहीं है this.bar
और इसलिए केवल थ्रेड 1 एक समय में givne बिंदु पर foo को इनिशियलाइज़ करने में सक्षम होगा? मेरा मतलब है, आप लॉक को फिर से जारी करने के बाद मूल्य की जांच करते हैं, जब भी वैसे भी थ्रेड से नया मान होना चाहिए 1
कभी-कभी, कंपाइलर एक फ़ील्ड को ऑप्टिमाइज़ करेगा और इसे स्टोर करने के लिए एक रजिस्टर का उपयोग करेगा। यदि थ्रेड 1 फ़ील्ड को लिखता है और कोई अन्य थ्रेड इसे एक्सेस करता है, क्योंकि अपडेट को एक रजिस्टर (और मेमोरी में नहीं) में संग्रहीत किया गया था, तो 2 थ्रेड को बासी डेटा मिलेगा।
आप वाष्पशील कीवर्ड के बारे में सोच सकते हैं कि कंपाइलर "मैं चाहता हूं कि आप इस मान को मेमोरी में स्टोर करें"। यह गारंटी देता है कि 2 थ्रेड नवीनतम मान प्राप्त करता है।
से MSDN : अस्थिर संशोधक आम तौर पर एक क्षेत्र है कि serialize उपयोग करने के लिए ताला बयान का उपयोग किए बिना एक से अधिक थ्रेड द्वारा पहुँचा है के लिए प्रयोग किया जाता है। वाष्पशील संशोधक का उपयोग यह सुनिश्चित करता है कि एक धागा दूसरे धागे द्वारा लिखे गए सबसे अद्यतित मूल्य को पुनः प्राप्त करता है।
सीएलआर निर्देशों का अनुकूलन करना पसंद करता है, इसलिए जब आप कोड में किसी फ़ील्ड का उपयोग करते हैं तो वह हमेशा फ़ील्ड के वर्तमान मूल्य तक नहीं पहुंच सकता है (यह स्टैक से हो सकता है, आदि)। किसी फ़ील्ड को चिह्नित करना volatile
यह सुनिश्चित करता है कि निर्देश द्वारा फ़ील्ड का वर्तमान मान एक्सेस किया गया है। यह तब उपयोगी होता है जब आपके प्रोग्राम के समवर्ती धागे या ऑपरेटिंग सिस्टम में चल रहे कुछ अन्य कोड द्वारा मान को संशोधित किया जा सकता है (गैर-लॉकिंग परिदृश्य में)।
आप स्पष्ट रूप से कुछ अनुकूलन खो देते हैं, लेकिन यह कोड को अधिक सरल रखता है।
जॉयदीप कांजीलाल का यह लेख मुझे बहुत मददगार लगा!
When you mark an object or a variable as volatile, it becomes a candidate for volatile reads and writes. It should be noted that in C# all memory writes are volatile irrespective of whether you are writing data to a volatile or a non-volatile object. However, the ambiguity happens when you are reading data. When you are reading data that is non-volatile, the executing thread may or may not always get the latest value. If the object is volatile, the thread always gets the most up-to-date value
मैं इसे केवल संदर्भ के लिए यहाँ छोड़ दूँगा
संकलक कभी-कभी इसे अनुकूलित करने के लिए कोड में बयानों का क्रम बदलता है। आम तौर पर यह एकल-थ्रेडेड वातावरण में कोई समस्या नहीं है, लेकिन यह बहु-थ्रेडेड वातावरण में एक समस्या हो सकती है। निम्न उदाहरण देखें:
private static int _flag = 0;
private static int _value = 0;
var t1 = Task.Run(() =>
{
_value = 10; /* compiler could switch these lines */
_flag = 5;
});
var t2 = Task.Run(() =>
{
if (_flag == 5)
{
Console.WriteLine("Value: {0}", _value);
}
});
यदि आप t1 और t2 चलाते हैं, तो आप परिणाम के रूप में कोई आउटपुट या "मान: 10" की अपेक्षा नहीं करेंगे। यह हो सकता है कि संकलक t1 फ़ंक्शन के अंदर स्विच करता है। यदि t2 तब निष्पादित होता है, तो यह हो सकता है कि _flag का मान 5 है, लेकिन _value में 0. है इसलिए अपेक्षित तर्क को तोड़ा जा सकता है।
इसे ठीक करने के लिए आप वाष्पशील कीवर्ड का उपयोग कर सकते हैं जिसे आप फ़ील्ड पर लागू कर सकते हैं। यह कथन कंपाइलर ऑप्टिमाइज़ेशन को अक्षम करता है ताकि आप सही क्रम को कोड में लागू कर सकें।
private static volatile int _flag = 0;
आपको वाष्पशील का उपयोग केवल तभी करना चाहिए जब आपको वास्तव में इसकी आवश्यकता हो, क्योंकि यह कुछ संकलक अनुकूलन को निष्क्रिय कर देता है, यह प्रदर्शन को नुकसान पहुंचाएगा। यह सभी .NET भाषाओं द्वारा समर्थित नहीं है (विजुअल बेसिक इसका समर्थन नहीं करता है), इसलिए यह भाषा की इंटरऑपरेबिलिटी में बाधा उत्पन्न करता है।
तो यह सब करने के लिए, प्रश्न का सही उत्तर है: यदि आपका कोड 2.0 रनटाइम या बाद में चल रहा है, तो अस्थिर कीवर्ड की लगभग कभी आवश्यकता नहीं होती है और अनावश्यक रूप से उपयोग किए जाने पर अच्छे से अधिक नुकसान होता है। IE कभी इसका इस्तेमाल न करें। रनटाइम के पुराने संस्करणों में BUT, इसे स्थिर क्षेत्रों पर उचित डबल चेक लॉकिंग के लिए आवश्यक है। विशेष रूप से स्थिर क्षेत्र जिनके वर्ग में स्थिर वर्ग आरंभीकरण कोड होता है।
कई धागे एक चर का उपयोग कर सकते हैं। नवीनतम अद्यतन चर पर होगा