2010-09-26 12 views
6

मैंने एक जेनेरिक निर्माता-उपभोक्ता कतार विकसित की है जो मॉनिटर द्वारा दालों को निम्न तरीके से विकसित किया गया है:मॉनीटर करता है। सिंक्रनाइज़ेशन की आवश्यकता है?

enqueue:

 public void EnqueueTask(T task) 
    { 
     _workerQueue.Enqueue(task); 
     Monitor.Pulse(_locker); 
    } 

डेक्यू:

private T Dequeue() 
    { 
     T dequeueItem; 
     if (_workerQueue.Count > 0) 
     { 
       _workerQueue.TryDequeue(out dequeueItem); 
      if(dequeueItem!=null) 
       return dequeueItem; 
     } 
     while (_workerQueue.Count == 0) 
      { 
       Monitor.Wait(_locker); 
     } 
     _workerQueue.TryDequeue(out dequeueItem); 
     return dequeueItem; 
    } 

प्रतीक्षा खंड निम्न सिंक्रनाइज़ेशन लॉक अपवाद उत्पन्न करता है : "ऑब्जेक्ट सिंक्रनाइज़ेशन विधि को कोड के एक सिंक्रनाइज़ किए गए ब्लॉक से बुलाया गया था" क्या मुझे इसे सिंक करने की आवश्यकता है? क्यूं कर ? क्या मैन्युअल रीसेट एवेन्ट्स या .NET 4.0 के स्लिम संस्करण का उपयोग करना बेहतर है?

उत्तर

6

हां, मौजूदा थ्रेड को Wait या Pulse पर कॉल करने के लिए मॉनीटर के "स्वामित्व" की आवश्यकता है। (इसलिए आपको Pulse के लिए भी लॉक करना होगा।) मुझे यह क्यों पता नहीं है कि इसकी आवश्यकता क्यों है, लेकिन यह जावा में समान है। मुझे आमतौर पर पता चला है कि मैं वैसे भी करना चाहता हूं, कॉलिंग कोड को साफ करने के लिए।

ध्यान दें कि Wait मॉनीटर को ही जारी करता है, फिर Pulse के लिए प्रतीक्षा करता है, फिर लौटने से पहले मॉनिटर को पुनः प्राप्त करता है।

बजाय ManualResetEvent या AutoResetEvent प्रयोग करने के लिए के रूप में - आप कर सकते थे, लेकिन जब तक मैं इंतजार हैंडल की अन्य सुविधाओं में से कुछ की जरूरत है (जैसे atomically किसी भी/एकाधिक हैंडल के सभी के लिए इंतज़ार कर के रूप में) व्यक्तिगत रूप से मैं Monitor तरीकों का उपयोग कर पसंद करते हैं।

+0

आप इसे क्यों पसंद करते हैं? मॉनिटर के लिए उपयोग किए गए लॉकर ऑब्जेक्ट पर बस एक लॉक मॉनिटर को कैसे सिंक्रनाइज़ करेगा? लॉक नहीं करते एक और संदर्भ स्विचिंग जो ResetEvents की आवश्यकता नहीं है? – user437631

+0

@ user437631: हां, बस एक सामान्य 'लॉक' कथन ठीक है। उसमें अतिरिक्त संदर्भ स्विच की आवश्यकता हो सकती है या नहीं - और मुझे नहीं लगता कि आपके पास कोई सबूत है कि ResetEvents को इसकी आवश्यकता नहीं होगी। वास्तव में, क्योंकि वे संभावित रूप से क्रॉस-प्रोसेस Win32 ऑब्जेक्ट्स की बजाय सीएलआर-आंतरिक ऑब्जेक्ट्स हैं, मॉनीटर रीसेट एवेन्ट्स की तुलना में लाइटर-प्रतीक्षा हैं। –

2

Monitor.Wait() की MSDN वर्णन से:

विज्ञप्ति ताला एक वस्तु पर और ब्लॉक वर्तमान धागा जब तक यह ताला reacquires।

'लॉक जारी करता है' भाग समस्या है, ऑब्जेक्ट लॉक नहीं है। आप _locker ऑब्जेक्ट का इलाज कर रहे हैं जैसे कि यह एक प्रतीक्षा हैडल है। अपना खुद का लॉकिंग डिज़ाइन करना जो कि सही रूप से सही है, वह काले जादू का एक रूप है जो हमारे मेडिसिन मैन, जेफरी रिचटर और जो डफी के लिए सबसे अच्छा है।

public class BlockingQueue<T> { 
    private Queue<T> queue = new Queue<T>(); 

    public void Enqueue(T obj) { 
     lock (queue) { 
      queue.Enqueue(obj); 
      Monitor.Pulse(queue); 
     } 
    } 

    public T Dequeue() { 
     T obj; 
     lock (queue) { 
      while (queue.Count == 0) { 
       Monitor.Wait(queue); 
      } 
      obj = queue.Dequeue(); 
     } 
     return obj; 
    } 
} 

सबसे किसी भी व्यावहारिक निर्माता/उपभोक्ता परिदृश्य आप निर्माता रुके तो यह कतार असीम नहीं भर सकते चाहते होगा: लेकिन मैं इस एक एक शॉट दे देंगे। उदाहरण के लिए डफी के BoundedBuffer design देखें। यदि आप .NET 4.0 पर जाने के लिए सक्षम हो सकते हैं तो आप निश्चित रूप से इसके ConcurrentQueue क्लास का लाभ लेना चाहते हैं, इसमें लो-ओवरहेड लॉकिंग और स्पिन-प्रतीक्षा के साथ बहुत अधिक काला जादू है।

0

देखने पर Monitor.Wait और Monitor.Pulse/PulseAll प्रणाली दे के साधन के रूप (Wait के लिए) प्रतीक्षा करने का एक साधन उपलब्ध कराने, बल्कि के रूप में नहीं है उचित तरीके से जानते हैं कि कोड वेटिंग पाश में है जो बाहर निकलने नहीं कर सकते ब्याज में कुछ बदलाव होने तक, और (Pulse/PulseAll के लिए) सिस्टम को यह बताने के साधन के रूप में कि कोड ने कुछ ऐसा बदल दिया है जो निकास की स्थिति को किसी अन्य थ्रेड के प्रतीक्षा लूप को संतुष्ट कर सकता है। Wait की सभी घटनाओं को Sleep(0) के साथ प्रतिस्थापित करने में सक्षम होना चाहिए और अभी भी कोड काम सही ढंग से हो सकता है (भले ही सीपीयू समय को बार-बार परीक्षण करने वाली स्थितियों का परीक्षण करने के परिणामस्वरूप बहुत कम कुशलतापूर्वक हो)।

  • प्रतीक्षा पाश में कोड हालत का परीक्षण करती है जब यह संतुष्ट नहीं है:

    इस तंत्र काम करने के लिए, यह निम्न क्रम की संभावना से बचने के लिए आवश्यक है।

  • अन्य धागे में कोड स्थिति को बदलता है ताकि यह संतुष्ट हो।

  • उस अन्य धागे में कोड लॉक को दाल करता है (जो कि अभी तक कोई भी इंतजार नहीं कर रहा है)।

  • प्रतीक्षा लूप में कोड Wait करता है क्योंकि इसकी स्थिति संतुष्ट नहीं थी।

Wait विधि की आवश्यकता है कि प्रतीक्षा धागा के बाद कि एक ही रास्ता यह सुनिश्चित करें कि हालत इस पर इंतजार कर रहा है समय यह परीक्षण किया है और बार कोड प्रदर्शन के बीच में परिवर्तन नहीं होगा हो सकता है, लॉक WaitPulse विधि को लॉक की आवश्यकता होती है क्योंकि यह एकमात्र तरीका है कि यह सुनिश्चित हो सकता है कि यदि कोई अन्य धागा Wait करने के लिए स्वयं "प्रतिबद्ध" है, तो Pulse अन्य धागे वास्तव में ऐसा करने के बाद तक नहीं होगा। ध्यान दें कि लॉक के भीतर Wait का उपयोग करने से यह गारंटी नहीं मिलती है कि इसका सही उपयोग किया जा रहा है, लेकिन लॉक के बाहर Wait का उपयोग करने का कोई तरीका नहीं है कि संभवतः सही हो सकता है।

Wait/Pulse डिज़ाइन वास्तव में दोनों पक्षों द्वारा सहयोग करने के लिए उचित रूप से अच्छी तरह से काम करता है। डिजाइन की सबसे बड़ी कमजोरियों, आईएमएचओ, (1) धागे के लिए इंतजार करने के लिए कोई तंत्र नहीं है जब तक कि कई वस्तुओं में से कोई भी स्पंदित न हो; (2) भले ही कोई ऑब्जेक्ट "शट डाउन" कर रहा हो, भले ही सभी भावी प्रतीक्षा लूप तुरंत बाहर निकलें (शायद बाहर निकलने के झंडे की जांच करके), यह सुनिश्चित करने का एकमात्र तरीका है कि कोई भी Wait जिस पर थ्रेड स्वयं ही प्रतिबद्ध है, उसे Pulse मिलेगा लॉक हासिल करना है, संभवतः इसके लिए उपलब्ध होने के लिए अनिश्चित काल तक प्रतीक्षा करना है।

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