2012-06-22 14 views
8

एक आम बात कॉल दौड़ की स्थिति से बचने के लिए (मल्टी-थ्रेडेड क्षुधा में) जब ट्रिगर घटनाओं यह है:थ्रेड सुरक्षित घटना

EventHandler<EventArgs> temp = SomeEvent; 
if (temp != null) temp(e); 

"Remember that delegates are immutable and this is why this technique works in theory. However, what a lot of developers don't realize is that this code could be optimized by the compiler to remove the local temp variable entirely. If this happens, this version of the code is identical to the first version, so a NullReferenceException is still possible." 

समस्या (पुस्तक के अनुसार) है कि "इस कोड अनुकूलित किया जा सकता है संकलक द्वारा पूरी तरह से स्थानीय अस्थायी चर दूर करने के लिए। यदि ऐसा होता है, कोड के इस संस्करण पहले संस्करण के समान है, इसलिए एक NullReferenceException अभी भी संभव है "

सी # के माध्यम से CLR के अनुसार, यहां के लिए एक बेहतर तरीका है इवेंट पॉइंटर की प्रतिलिपि बनाने के लिए कंपाइलर को मजबूर करें।

virtual void OnNewMail(NewMailEventArgs e) 
{ 
    EventHandler<NewMailEventArgs> temp = 
          Interlocked.CompareExchange(ref NewMail, null, null); 
    if (temp != null) 
     temp(this, e); 
} 

यहाँ, CompareExchange अशक्त करने के लिए चलाएँ संदर्भ में परिवर्तन अगर यह रिक्त है और चलाएँ में परिवर्तन नहीं करता है, तो यह शून्य नहीं है। दूसरे शब्दों में, तुलना एक्सचेंज न्यूमेल में मूल्य को बिल्कुल नहीं बदलता है, लेकिन यह न्यूमेल के अंदर मूल्य को परमाणु, थ्रेड-सुरक्षित तरीके से वापस कर देता है। रिक्टर, जेफरी (2010-02-12)। सी # के माध्यम से सीएलआर (पृष्ठ 265)। ओर्ली मीडिया - ए किंडल संस्करण।

मैं नेट 4.0 ढांचे पर हूँ, और यकीन है कि यह कैसे संभवतः काम कर सकते हैं, क्योंकि Interlocked.CompareExchange एक स्थान है, न कि घटना के लिए एक संदर्भ के लिए एक संदर्भ की उम्मीद नहीं।

या तो पुस्तक में कोई त्रुटि है, या मैंने इसे गलत व्याख्या की है। क्या किसी ने इस विधि को लागू किया है? या यहां दौड़ की स्थिति को रोकने के लिए एक बेहतर तरीका है?

अद्यतन

यह मेरी गलती थी, iterlocked कोड काम करता है। मैंने अभी गलत कास्टिंग निर्दिष्ट किया था, लेकिन ब्रैडली (नीचे) के अनुसार यह .NET 2.0 और विंडोज़ पर आवश्यक नहीं है।

+0

http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx दिलचस्प हो सकता है – Vlad

उत्तर

7

कंपाइलर (या जेआईटी) को if/temp दूर (सीएलआर 2.0 और बाद में) अनुकूलित करने की अनुमति नहीं है; CLR 2.0 Memory Model ढेर से पढ़ने की अनुमति नहीं देता है (नियम # 2)।

इस प्रकार, MyEvent दूसरी बार पढ़ा नहीं जा सकता; temp का मान if कथन में पढ़ा जाना चाहिए।

इस स्थिति की विस्तृत चर्चा के लिए my blog post देखें, और मानक पैटर्न ठीक क्यों है, इसका एक स्पष्टीकरण।

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

अपने प्रश्न के संबंध में के बारे में Interlocked.CompareExchange, वाक्य रचना public event EventHandler<NewMailEventArgs> NewMail बस सी # प्रकार EventHandler<NewMailEventArgs> के एक निजी क्षेत्र और एक सार्वजनिक समारोह है कि add और remove तरीकों घोषणा के लिए वाक्यात्मक चीनी है। Interlocked.CompareExchange कॉल निजी EventHandler<NewMailEventArgs> फ़ील्ड का मान पढ़ता है, इसलिए यह कोड संकलित करता है और रिचटर का वर्णन करता है; यह माइक्रोसॉफ्ट सीएलआर में सिर्फ अनावश्यक है।

+0

मैंने लेखक से टिप्पणियों के साथ अपना उदाहरण अपडेट किया। यदि आप जो कह रहे हैं वह सच है, तो जेफरी रिचटर क्या कह रहा है उससे सीधे संघर्ष करता है।और मैंने उसे पहले इस पुस्तक में गलत बयान के साथ पकड़ा है .. मैं बस ऐसा करने के सही तरीके की पुष्टि करना चाहता हूं .. –

+0

केवल विंडोज़/नेट शिकायतकर्ता/जेआईटी में, यह मोनो के लिए असत्य है। http://www.ecma-international.org/publications/standards/Ecma-335.htm – caesay

+0

@caesay: अच्छा बिंदु; मैंने संबोधित किया कि मेरे ब्लॉग पोस्ट में, लेकिन उत्तर में नहीं; मैं अपडेट करूंगा। –

1

मुझे लगता है कि आपको याद आती है। स्थान का मतलब केवल ऑब्जेक्ट को संदर्भित करने के लिए एक सूचक [msdn संस्करण: गंतव्य ऑब्जेक्ट की तुलना तुलना और तुलना में की जाती है।]। कोड के बाद ठीक काम करता है। 4।0

public class publisher 
{ 

    public event EventHandler<EventArgs> TestEvent; 
    protected virtual void OnTestEvent(EventArgs e) 
    { 
     EventHandler<EventArgs> temp = Interlocked.CompareExchange(ref TestEvent, null, null); 
     if (temp != null) 
      temp(this,e); 
    } 
} 
3

अब इस क्योंकि मैं Interlocked.CompareExchange के प्रयोग पर टिप्पणी नहीं कर सकता, फिर भी मुझे लगता है कि इस जानकारी को उपयोगी हो सकता है, केवल अपने प्रश्न का एक आंशिक जवाब है।

समस्या

कि संकलक का अनुकूलन कर सकते हैं यह है कि अगर/अस्थायी दूर,

खैर, सी # (पी। 264-265)

[के माध्यम से CLR के अनुसार ] स्थानीय [...] चर पूरी तरह से हटाने के लिए संकलक द्वारा कोड अनुकूलित किया जा सकता है। यदि ऐसा होता है, तो कोड का यह संस्करण [संस्करण जो संदर्भ दो बार संदर्भित करता है] के समान है, इसलिए एक NullReferenceException अभी भी संभव है।

इसलिए, यह हालांकि, यह महत्वपूर्ण है पता है कि माइक्रोसॉफ्ट के जस्ट-इन-समय (JIT) संकलक कभी स्थानीय चर दूर अनुकूलन नहीं करता संभव है। हालांकि यह संभवतः बदल सकता है, यह असंभव है, क्योंकि यह शायद बहुत सारे अनुप्रयोगों को तोड़ देगा।

इसका कारण यह है नेट एक मजबूत स्मृति मॉडल हैं: http://msdn.microsoft.com/en-us/magazine/cc163715.aspx#S5

रीड और राईट पेश नहीं किया जा सकता। क्योंकि इस स्मृति से एक मूल्य refetching अर्थ होगा

और

मॉडल की अनुमति नहीं है, शुरू की लेकिन पढ़ता है, और कम लॉक कोड स्मृति में बदल रहा है हो सकता है।

 

हालांकि, मोनो, जो एक much weaker memory model इस प्रकार है, कि स्थानीय चर दूर का अनुकूलन कर सकते हैं।

नीचे पंक्ति: जब तक आप मोनो का उपयोग करने की योजना बना रहे हैं, तो इसके बारे में चिंता न करें।

और फिर भी, उस व्यवहार को अस्थिर घोषणाओं से दबाया जा सकता है।

+0

आप सही हैं .. मैं वहां एक निष्कर्ष पर कूद गया। मेरी पोस्ट को समायोजित किया और हटा दिया कि "अनुकूलित दूर" कथन। –

0

मैं तुम्हें आईएल को देखने का उत्पादन आप देखते हैं कि ldsflda देखेंगे कि विधि इस

IL_000d: ldsflda class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs> ConsoleApplication1.Program::MyEvent 
IL_0012: ldnull 
IL_0013: ldnull 
IL_0014: call  !!0 [mscorlib]System.Threading.Interlocked::CompareExchange<class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs>>(!!0&,!!0,!!0) 

की तरह कहा जाता है - मेरी घटना स्थिर है लेकिन यह एक क्षेत्र का पता लोड हो रहा है। वह क्षेत्र ऑटो जनरेटेड प्रतिनिधि क्षेत्र है जो संकलक प्रत्येक घटना के लिए उत्पन्न करता है।

क्षेत्र इस तरह परिभाषित किया गया है:

.field private static class [mscorlib]System.EventHandler`1<class [mscorlib]System.EventArgs> MyEvent 
संबंधित मुद्दे