2010-12-16 23 views
6

मेरे पास एक निर्माता/उपभोक्ता कतार है, सिवाय इसके कि विशिष्ट प्रकार की वस्तुएं हैं। इसलिए कोई भी उपभोक्ता एक अतिरिक्त वस्तु का उपभोग नहीं कर सकता है। मैं प्रत्येक प्रकार के लिए एक विशिष्ट कतार बनाना नहीं चाहता, क्योंकि बहुत सारे हैं। (यह उत्पादक/उपभोक्ता की परिभाषा को फैलाता है, लेकिन मुझे यकीन नहीं है कि सही शब्द क्या है।)सी # निर्माता/उपभोक्ता/पर्यवेक्षक?

क्या इवेंटवाइटहैंडल जैसी कोई चीज है जो दालों को पैरामीटर के साथ अनुमति देती है? जैसे myHandle.Set(AddedType = "foo")। अभी मैं Monitor.Wait का उपयोग कर रहा हूं और फिर प्रत्येक उपभोक्ता यह देखने के लिए जांच करता है कि नाड़ी वास्तव में उनके लिए बनाई गई थी, लेकिन ऐसा लगता है कि यह व्यर्थ है।

अब मैं क्या है की एक pseduocode संस्करण:

class MyWorker { 
    public string MyType {get; set;} 
    public static Dictionary<string, MyInfo> data; 

    public static void DoWork(){ 
     while(true){ 
      if(Monitor.Wait(data, timeout)){ 
        if (data.ContainsKey(MyType)){ 
         // OK, do work 
        } 
      } 
     } 
    } 
} 

आप देख सकते हैं, मैं दालों मिल सकता है जब अन्य सामान dict में जोड़ा जाता है। मुझे केवल तब परवाह है जब माईटाइप को निर्देश में जोड़ा जाता है। क्या ऐसा करने के लिए कोई रास्ता है? यह एक बड़ा सौदा नहीं है, लेकिन, उदाहरण के लिए, मुझे मैन्युअल रूप से टाइमआउट्स को मैन्युअल रूप से संभालना होगा, क्योंकि प्रत्येक लॉक टाइमआउट के भीतर सफल हो सकता है, लेकिन MyType को timeout के भीतर कभी भी नहीं जोड़ा गया है।

+0

सिर्फ एक विचार, क्या प्रत्येक ऑब्जेक्ट प्रकार की अपनी लॉक ऑब्जेक्ट हो सकती है, ताकि आप उचित ऑब्जेक्ट को 'मॉनिटर कर सकें।' –

+0

@deltreme: हाँ, यह संभव है, लेकिन मैं प्रत्येक प्रकार के लिए एक नई वस्तु नहीं बनाना चाहता। – Xodarap

+0

मुझे यकीन नहीं है कि प्रत्येक के लिए अलग-अलग कतार बनाने के लिए बहुत से प्रकार होने के कारण आपका क्या मतलब है। ऐसा कैसे? यह करने का यह स्वाभाविक तरीका है, और मुझे यकीन नहीं है कि कितनी अलग कतारें किसी अन्य घटना-हैंडलिंग तंत्र की तुलना में कम कुशल होंगी। –

उत्तर

3

यह एक दिलचस्प सवाल है। ऐसा लगता है कि समाधान की कुंजी priority queue का एक अवरुद्ध संस्करण है। जावा में PriorityBlockingQueue है, लेकिन दुर्भाग्यवश .NET BCL के बराबर कोई भी नहीं है। एक बार आपके पास एक बार, कार्यान्वयन आसान है।

class MyWorker 
{ 
    public string MyType {get; set;} 
    public static PriorityBlockingQueue<string, MyInfo> data; 

    public static void DoWork() 
    { 
     while(true) 
     { 
      MyInfo value; 
      if (data.TryTake(MyType, timeout, out value)) 
      { 
       // OK, do work 
      } 
     } 
    } 
} 

PriorityBlockingQueue लागू करना बहुत मुश्किल नहीं है। Add और Take शैली विधियों का उपयोग करके BlockingCollection के समान पैटर्न के बाद मैं निम्नलिखित कोड के साथ आया था।

public class PriorityBlockingQueue<TKey, TValue> 
{ 
    private SortedDictionary<TKey, TValue> m_Dictionary = new SortedDictionary<TKey,TValue>(); 

    public void Add(TKey key, TValue value) 
    { 
     lock (m_Dictionary) 
     { 
      m_Dictionary.Add(key, value); 
      Monitor.Pulse(m_Dictionary); 
     } 
    } 

    public TValue Take(TKey key) 
    { 
     TValue value; 
     TryTake(key, TimeSpan.FromTicks(long.MaxValue), out value); 
     return value; 
    } 

    public bool TryTake(TKey key, TimeSpan timeout, out TValue value) 
    { 
     value = default(TValue); 
     DateTime initial = DateTime.UtcNow; 
     lock (m_Dictionary) 
     { 
      while (!m_Dictionary.TryGetValue(key, out value)) 
      { 
       if (m_Dictionary.Count > 0) Monitor.Pulse(m_Dictionary); // Important! 
       TimeSpan span = timeout - (DateTime.UtcNow - initial); 
       if (!Monitor.Wait(m_Dictionary, span)) 
       { 
        return false; 
       } 
      } 
      m_Dictionary.Remove(key); 
      return true; 
     } 
    } 
} 

यह एक त्वरित कार्यान्वयन था और इसमें कुछ समस्याएं थीं। सबसे पहले, मैंने इसका परीक्षण नहीं किया है। दूसरा, यह अंतर्निहित डेटा संरचना के रूप में एक लाल-काले पेड़ (SortedDictionary के माध्यम से) का उपयोग करता है। इसका मतलब है कि TryTake विधि में ओ (लॉग (एन)) जटिलता होगी। प्राथमिकता कतारों में आम तौर पर ओ (1) हटाने की जटिलता होती है। प्राथमिकता कतारों के लिए पसंद की आम तौर पर डेटा संरचना heap है, लेकिन मुझे लगता है कि skip lists वास्तव में कई कारणों से अभ्यास में बेहतर है। इनमें से कोई भी .NET BCL में मौजूद नहीं है, इसीलिए मैंने इस परिदृश्य में इसके निचले प्रदर्शन के बावजूद SortedDictionary का उपयोग किया।

मुझे यह इंगित करना चाहिए कि यह वास्तव में व्यर्थ Wait/Pulse व्यवहार को हल नहीं करता है। यह बस PriorityBlockingQueue कक्षा में encapsulated है। लेकिन, कम से कम यह निश्चित रूप से आपके कोड के मूल भाग को साफ करेगा।

यह आपके कोड की तरह कई ऑब्जेक्ट्स को संभालने की तरह दिखाई नहीं दे रहा था, लेकिन शब्दकोश में जोड़ते समय एक सादे पुराने MyInfo के बजाय Queue<MyInfo> का उपयोग करके जोड़ना आसान होगा।

+0

दिलचस्प, अच्छा विचार यह encapsulate करने के लिए। क्या आपके पास सिर्फ 'शब्दकोश' के बजाय 'सॉर्टेड डिक्शनरी' का उपयोग करने का कोई कारण है? बस जल्दी सम्मिलित समय? – Xodarap

+0

@Xodarap: ठीक है, अच्छा सवाल है। मैं मूल रूप से सोच रहा था कि आपको 'टेक' अधिभार की आवश्यकता होगी जिसने एक कुंजी स्वीकार नहीं की है और यह शब्दकोश के पहले आइटम को हटा देगा। यद्यपि सही तरीके से ऐसा करने के लिए आपको एक क्रमबद्ध शब्दकोश की आवश्यकता है। वास्तव में यह काम करने के लिए एक प्राथमिकता कतार माना जाता है। मुझे लगता है कि आप एक सादे पुराने शब्दकोश का उपयोग कर सकते हैं और इसके बजाय रैपर वर्ग 'ब्लॉकिंग डिक्शनरी' को कॉल कर सकते हैं। अच्छा पकड़ा। –

1

ऐसा लगता है कि आप एक पर्यवेक्षक पैटर्न के साथ निर्माता/उपभोक्ता कतार को जोड़ना चाहते हैं - जेनेरिक उपभोक्ता थ्रेड या धागे कतार से पढ़ते हैं, और फिर ईवेंट को आवश्यक कोड में पास करते हैं। इस उदाहरण में आप वास्तव में पर्यवेक्षक को संकेत नहीं देंगे, लेकिन उपभोक्ता धागा पहचानता है कि किसी दिए गए कार्य आइटम में रुचि रखने वाले व्यक्ति को यह पता चलता है।

नेट में पर्यवेक्षक पैटर्न आमतौर पर सी # घटनाओं का उपयोग करके लागू किया जाता है। आपको ऑब्जेक्ट के लिए इवेंट हैंडलर को कॉल करने की आवश्यकता होगी और एक या अधिक पर्यवेक्षकों को इसके माध्यम से बुलाया जाएगा। लक्ष्य कोड को पहले काम के आगमन पर अधिसूचना के लिए खुद को जोड़कर मनाए गए ऑब्जेक्ट के साथ खुद को पंजीकृत करना होगा।

+0

ऐसा लगता है कि मेरे पास 'शब्दकोश <प्रकार, pulseObject>' होना चाहिए और ''टाइप' अपडेट होने पर' मॉनीटर.पल्स (शब्दकोश [प्रकार]) 'बस करें? ऐसा लगता है जैसे मैं पर्यवेक्षक को हटा सकता हूं और इसे निर्माता से मैन्युअल रूप से कर सकता हूं। (मैं मानता हूं कि यह काम करेगा, लेकिन मैं उम्मीद कर रहा था कि मैं उपभोक्ताओं के पीछे के दृश्यों का संग्रह कर सकता हूं।) – Xodarap

+0

यदि आप लक्षित ऑब्जेक्ट को सक्रिय करने के लिए 'मॉनिटर' का उपयोग कर रहे हैं तो कतार के माध्यम से एक्सेस किए गए एक अलग उपभोक्ता थ्रेड को अनावश्यक लगता है , पक्का। –