2009-08-31 16 views
28

मैं यह धारणा थी, पढ़ने this article कि यह बेहतर है धागा तुल्यकालन के लिए मॉनिटर/लॉक का उपयोग करने के रूप में यह देशी संसाधनोंमॉनिटर बनाम WaitHandle आधारित धागा सिंक

उपयोग नहीं करता है के बाद

विशिष्ट बोली (लेख के पेज 5 से):

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

कुछ लोग यह जानकर आश्चर्यचकित हो सकते हैं कि इन कक्षाओं का उपयोग विभिन्न मॉनिटर विधियों का उपयोग करने से काफी धीमा हो सकता है। मेरा मानना ​​है कि ऐसा इसलिए है क्योंकि देशी Win32 कॉल में प्रबंधित कोड का "आउट" जा रहा है और फिर "इन" वापस फिर से मॉनिटर प्रदान करने वाली चीज़ों के पूरी तरह से प्रबंधित दृश्य की तुलना में महंगा है। एक पाठक ने यह भी समझाया है कि मॉनीटर उपयोगकर्ता मोड में लागू किए जाते हैं, जबकि प्रतीक्षा हैंडल का उपयोग कर्नेल मोड में स्विच करने की आवश्यकता होती है, जो काफी महंगा है।

लेकिन एसओ की खोज करने और यहां कुछ प्रश्न/उत्तर पढ़ने के बाद से मैंने प्रत्येक का उपयोग कब करने की मेरी समझ पर संदेह करना शुरू कर दिया है। ऐसा लगता है कि कई लोग ऐसे मामलों में ऑटो/मैनुअल रीसेट इवेंट का उपयोग करने की सलाह देते हैं जहां मॉनिटर। वैट/पल्स करेंगे। क्या कोई मुझे बता सकता है जब मॉनिटर पर WaitHandle आधारित सिंक का उपयोग किया जाना चाहिए?

धन्यवाद

उत्तर

51

एक समस्या Monitor.Pulse/Wait साथ कि संकेत खो दिया हो सकता है।

उदाहरण के लिए:

var signal = new ManualResetEvent(false); 

// Thread 1 
signal.WaitOne(); 

// Thread 2 
signal.Set(); 

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

var signal = new object(); 

// Thread 1 
lock (signal) 
{ 
    Monitor.Wait(signal); 
} 

// Thread 2 
lock (signal) 
{ 
    Monitor.Pulse(signal); 
} 

यहाँ संकेत (Pulse) यदि PulseWait से पहले निष्पादित किया जाता है खो दिया हो जाएगा:

अब एक मॉनिटर का उपयोग कर एक ही उदाहरण पर एक नजर है।

इस समस्या को दूर करने के लिए आपको कुछ इस तरह की जरूरत है:

var signal = new object(); 
var signalSet = false; 

// Thread 1 
lock (signal) 
{ 
    while (!signalSet) 
    { 
     Monitor.Wait(signal); 
    } 
} 

// Thread 2 
lock (signal) 
{ 
    signalSet = true; 
    Monitor.Pulse(signal); 
} 

यह काम करता है और शायद भी अधिक performant और हल्के है, लेकिन कम पढ़ी जा सकती है। और यह वह जगह है जहां सिरदर्द को संयम कहा जाता है।

  • इस कोड को वास्तव में काम करता है?
  • हर कोने मामले में?
  • दो से अधिक धागे के साथ? (संकेत: यह करता है नहीं)
  • कैसे आप इकाई परीक्षण यह करते हैं?

कच्चे प्रदर्शन से अक्सर एक ठोस, विश्वसनीय, पठनीय अमूर्तता बेहतर होती है।

साथ ही, WaitHandles आदि स्थापित किया जाना हैंडल का एक सेट के लिए इंतज़ार कर, को लागू करने पर नज़र रखता है के साथ इस सिर दर्द और भी बदतर बना देता है जैसे कुछ अच्छा सामान प्रदान करते हैं ... अंगूठे का


नियम:

  • उपयोग मॉनिटर्स (lock) एक साझा संसाधन
  • उपयोग डब्ल्यू के लिए अनन्य पहुँच सुनिश्चित करने के लिए aitHandles (मैनुअल/AutoResetEvent/सेमाफोर) धागे
+0

बहुत अच्छी व्याख्या। –

+0

इसे समझाने के लिए धन्यवाद, यह सही समझ में आता है। – Matt

+0

मुझे इस जगह से प्यार है ... आप इस तरह आसानी से ढूंढ सकते हैं और जवाब दे सकते हैं जो आपको पुस्तकों और लेखों के माध्यम से शोध करने के घंटों बचाएगा ... –

3

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

मेरे पास कुछ कोड है जहां थ्रेड ए को नेटवर्क संदेश प्राप्त होते हैं, उन्हें घेरते हैं, फिर दालें थ्रेड बी थ्रेड बी ताले, किसी भी संदेश को हटाते हैं, कतार को अनलॉक करते हैं, फिर संदेशों को संसाधित करते हैं।

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

ध्यान दें कि इस समस्या वास्तव में, थोड़ी देर के लिए सतह नहीं था मूल रूप से अपने पूरे लूप के रूप में था की तरह कुछ:

while (keepgoing) 
    { 
    lock (messageQueue) 
     { 
     while (messageQueue.Count > 0) 
      ProcessMessages(messageQueue.DeQueue()); 

     Monitor.Wait(messageQueue); 
     } 
    } 

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

-1

@ विल गोर के मामले के लिए, यह हमेशा अच्छा है, कतार को तब तक संसाधित करना जारी रखना एक अच्छा अभ्यास है, इससे पहले कॉलिंग मॉनिटर.एट। उदा .:

while (keepgoing) 
{ 
    List<Message> nextMsgs = new List<Message>(); 
    lock (messageQueue) 
    { 
    while (messageQueue.Count == 0) 
    { 
     try 
     { 
      Monitor.Wait(messageQueue); 
     } 
     catch(ThreadInterruptedException) 
     { 
      //... 
     } 
    } 
    while (messageQueue.Count > 0) 
     nextMsgs.Add(messageQueue.DeQueue()); 
    } 
    if(nextMsgs.Count > 0) 
    ProcessMessages(nextMsgs); 
} 

यह आपके द्वारा मिले मुद्दे को हल करना चाहिए, और लॉकिंग समय को कम करना चाहिए (बहुत महत्वपूर्ण!)।

+0

परिवर्तनीय 'nextMsg' घोषित किया गया है, लेकिन कभी असाइन नहीं किया गया है। 'संदेश' घोषित नहीं किया गया है, लेकिन असाइन किया गया है। यदि दो चर समान हैं, तो केवल * अंतिम * संदेश संसाधित किया जाएगा - बाकी को त्याग दिया जाएगा (सुंदर मूल तर्क बग)। 'मॉनिटर.एट' का उपयोग 'लॉक' स्कोप के बाहर किया जाता है, संभावित रूप से कतार को हमेशा लॉक कर देता है। यह बहुत बुरा है। –

+0

धन्यवाद Kirill Shlenskiy गलतियों को इंगित करने और उन लोगों के बारे में खेद है। पहले से ही नियत है। मैं पिछली पोस्ट की 2 चिंताओं को हल करने के विचार को दिखाने की कोशिश कर रहा था, लेकिन बहुत सावधान नहीं था। जब मॉनिटर। वैट लॉक के बाहर प्रयोग किया जाता है, तो यह वास्तव में सिंक्रनाइज़ेशन लॉकएक्सप्शन फेंक देगा। – Jim

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