2009-06-23 11 views
15
public void MyTest() 
{ 
    bool eventFinished = false; 

    myEventRaiser.OnEvent += delegate { doStuff(); eventFinished = true; }; 
    myEventRaiser.RaiseEventInSeperateThread() 

    while(!eventFinished) Thread.Sleep(1); 

    Assert.That(stuff); 
} 

क्यों अस्थिर हो eventFinished नहीं कर सकते हैं और यह फर्क पड़ता है?क्यों एक स्थानीय चर सी # में अस्थिर नहीं किया जा सकता?

यह मेरे लिए प्रतीत होता है कि इस मामले में संकलक या क्रम अपने अच्छे के लिए स्मार्ट करने के लिए हो सकता है और जब तक पाश कि eventFinished केवल झूठे हो सकता है में 'पता' कहते हैं। खासकर जब आप जिस तरह से विचार करना एक को उठा लिया चर एक वर्ग के एक सदस्य और कहा कि एक ही कक्षा के एक तरीके के रूप में प्रतिनिधि और तथ्य यह है कि एक बार eventFinished एक स्थानीय चर था की जिससे वंचित अनुकूलन के रूप में उत्पन्न हो जाता है।

+2

आपका चर इस मामले में स्थानीय नहीं है! इसके बजाय, यह एक कंपाइलर से उत्पन्न कक्षा में एक आवृत्ति चर है। –

+4

यह एक अर्थपूर्ण अंतर है - कोड के संदर्भ में, यह एक स्थानीय चर है जो कब्जा करने के लिए होता है ... –

+1

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

उत्तर

12

एक सूत्रण आदिम, ManualResetEvent ठीक इस कार्य को करने के लिए मौजूद है - आप एक बूलियन ध्वज का उपयोग करने के लिए नहीं करना चाहती।

कुछ इस तरह काम करना चाहिए:

public void MyTest() 
{ 
    var doneEvent = new ManualResetEvent(false); 

    myEventRaiser.OnEvent += delegate { doStuff(); doneEvent.Set(); }; 
    myEventRaiser.RaiseEventInSeparateThread(); 
    doneEvent.WaitOne(); 

    Assert.That(stuff); 
} 

स्थानीय चर, मैं नहीं मानता कि कोई कारण है कि इस सिद्धांत में नहीं संभव हो सकता है है पर volatile कीवर्ड के लिए समर्थन की कमी के बारे में सी # में। सबसे अधिक संभावना है, यह केवल समर्थित नहीं है क्योंकि सी # 2.0 से पहले ऐसी सुविधा के लिए कोई उपयोग नहीं था। अब, अज्ञात विधियों और लैम्ब्डा कार्यों के अस्तित्व के साथ, इस तरह का समर्थन संभावित रूप से उपयोगी हो सकता है। अगर कोई यहां कुछ याद कर रहा है तो कृपया कोई फर्क नहीं पड़ता।

+2

जब आपको उसकी आवश्यकता होती है तो एरिक कहां है? ; -पी –

+0

हेहे। दरअसल, हम अब सी #/सीएलआर के धुंधले इलाकों में जा रहे हैं - जिस पर मुझे यकीन है कि वह कुछ प्रकाश डाल सकता है। – Noldorin

+0

मैनुअल रीसेट इवेंट के लिए धन्यवाद। यह तब भी काम करता है जब घटना को एक ही धागे में बुलाया जाता है, जो एक संभावना है कि मैं MyTest() में बाहर नहीं जाना चाहता था। –

1

अगर घटना उठाया जब तक के बाद प्रक्रिया है कि स्थानीय चर का दायरा से बाहर निकल गया था पूरी नहीं हुई तो क्या होगा? चर जारी किया गया होगा और आपका धागा असफल हो जाएगा।

समझदार दृष्टिकोण एक प्रतिनिधि समारोह को संलग्न करना है जो उप-धागे को पूरा करने वाले पैरेंट थ्रेड को इंगित करता है।

+0

कोड ठीक है; यह एक "कब्जा" चर है, और एक कंपाइलर से उत्पन्न वर्ग पर एक क्षेत्र के रूप में लागू किया गया है। धागा असफल नहीं होगा। –

+0

क्या क्लास-स्तरीय चर के साथ यह वही मामला नहीं हो सकता है, हालांकि? यदि उदाहरण अन्य थ्रेड में घटना से पहले कचरा-एकत्रित होता है, तो वही समस्या होती है। – Noldorin

+0

यह कचरा नहीं हो सकता है, जबकि यह अभी भी धागे के दृश्यमान क्षेत्र में है। –

10

में परिदृश्य, स्थानीय चर धागे के लिए विशिष्ट हैं, इसलिए volatile से जुड़े मुद्दे पूरी तरह से अनावश्यक हैं।

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

विशेष रूप से Monitor (उर्फ lock) जैसे Pulse आदि के साथ कुछ ऐसा ही कर सकता है, जैसा कि अन्य थ्रेडिंग संरचनाओं की संख्या हो सकती है।

थ्रेडिंग मुश्किल है, और एक सक्रिय पाश शायद ही कभी यह प्रबंधन करने के लिए सबसे अच्छा तरीका है ...


पुन संपादित करें ... secondThread.Join() स्पष्ट बात हो सकता है - लेकिन तुम सच में उपयोग करना चाहते हैं एक अलग टोकन, नीचे देखें। इसका लाभ (ManualResetEvent जैसी चीजों पर) यह है कि इसे ओएस से कुछ भी आवश्यकता नहीं है - इसे पूरी तरह से सीएलआई के अंदर संभाला जाता है।

using System; 
using System.Threading; 
static class Program { 
    static void WriteLine(string message) { 
     Console.WriteLine(Thread.CurrentThread.Name + ": " + message); 
    } 
    static void Main() { 
     Thread.CurrentThread.Name = "Main"; 
     object syncLock = new object(); 
     Thread thread = new Thread(DoStuff); 
     thread.Name = "DoStuff"; 
     lock (syncLock) { 
      WriteLine("starting second thread"); 
      thread.Start(syncLock); 
      Monitor.Wait(syncLock); 
     } 
     WriteLine("exiting"); 
    } 
    static void DoStuff(object lockHandle) { 
     WriteLine("entered"); 

     for (int i = 0; i < 10; i++) { 
      Thread.Sleep(500); 
      WriteLine("working..."); 
     } 
     lock (lockHandle) { 
      Monitor.Pulse(lockHandle); 
     } 
     WriteLine("exiting"); 
    } 
} 
+0

क्या होगा यदि RaiseEventInSeperateThread() को प्रभावी रूप से कार्यान्वित किया गया था: नया थ्रेड (() => {थ्रेड.sleep (100); ऑनवेन्ट();}; आप मॉनिटर का उपयोग कैसे करेंगे या MyTest() इवेंट प्रतिनिधि को निष्पादित करने के लिए प्रतीक्षा करें? –

+0

मेरा मतलब है: नया थ्रेड (() => {थ्रेड.sleep (100); ऑनवेन्ट();})। प्रारंभ करें(); –

+0

आपके पास एक थ्रेड WaitOne होगा और अन्य पल्स। मैं बाद में एक उदाहरण जोड़ने की कोशिश करूंगा ... –

4

यदि आप स्थानीय var को अस्थिर के रूप में व्यवहार करना चाहते हैं तो आप Voltile.Write का भी उपयोग कर सकते हैं। जैसा कि:

public void MyTest() 
{ 
    bool eventFinished = false; 

    myEventRaiser.OnEvent += delegate { doStuff(); Volatile.Write(ref eventFinished, true); }; 
    myEventRaiser.RaiseEventInSeperateThread() 

    while(!Volatile.Read(eventFinished)) Thread.Sleep(1); 

    Assert.That(stuff); 
} 
+1

अच्छा जवाब लेकिन आपका 'while' लूप' Volatile.Read' का उपयोग करना चाहिए – CoderBrien

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