2011-01-18 17 views
5

हमने सी # Queue का उपयोग करके एक संदेश कतार लागू की है। हम जानते हैं कि हमारे पास while लूप के साथ प्रोसेसिंग के लिए कतार से उपलब्ध संदेश लेने के लिए केवल एक उपभोक्ता है। हम यह भी जानते हैं कि कतार में संदेश डालने के लिए केवल एक निर्माता है।संदेश कतार सोच

हम एक lock ऊपर संदेश कतार पर यकीन है कि उपभोक्ता और निर्माता एक साथ कतार उपयोग नहीं कर सकते बनाने के लिए किया है।

मेरा प्रश्न यह है कि lock आवश्यक है? यदि QueueCount संपत्ति के बाद एक आइटम वास्तव में जोड़ा गया है और यदि उपभोक्ता Count पुनर्प्राप्त करने से पहले जांचता है, तो उपभोक्ता को एक पूर्ण संदेश वस्तु मिलनी चाहिए, यहां तक ​​कि हमारे पास lock नहीं है। सही? इसलिए हमें आंशिक संदेश आइटम समस्या का सामना नहीं करना पड़ेगा। तो हम उस lock से छुटकारा पा सकते हैं?

lock सिस्टम को धीमा कर देगा और कभी-कभी हम देख सकते हैं कि पुनर्प्राप्ति धागा थोड़ी देर के लिए अवरुद्ध है क्योंकि हमारे पास बहुत भारी उत्पादक है।

संपादित करें:

दुर्भाग्य से हम नेट 3.5 का उपयोग कर रहे हैं।

+1

आप .NET 4 का उपयोग कर सकते हैं, तो यह एक 'BlockingCollection है' और एक 'ConcurrentQueue', प्रदर्शन के लिए के रूप में, http: //geekswithblogs.net/BlackRabbitCoder/archive/2010/06/07/c-system.collections.concurrent.concurrentqueue-vs.-queue.aspx –

+0

यदि आपके पास प्रदर्शन समस्याएं हैं, तो क्या आप कुछ कोड पोस्ट कर सकते हैं। साथ ही जब आप कहते हैं कि निर्माता भारी है, तो प्रति सेकेंड आइटम कितना भारी है? –

उत्तर

10

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

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

इसलिए, को कतारबद्ध \ दौरान विपंक्ति आप इन आपरेशन बनाने के लिए लॉक करनी तार्किक परमाणु दिखाई देते हैं।

आप .NET 4.0 में नए ConcurrentQueue कक्षा को आजमा सकते हैं क्योंकि संभवतः इसमें बेहतर प्रदर्शन विशेषताओं हैं क्योंकि यह गैर-लॉकिंग एल्गोरिदम का उपयोग करता है।

4

ताला, आवश्यक है। ConcurrentQueue<T> के साथ आप इसे आसानी से हटाकर आसानी से हटा सकते हैं, हालांकि, आप इसे BlockingCollection<T> के साथ बदलकर इस कोड को सरल बनाने पर विचार करना चाहेंगे।

यह आपके उपभोक्ता ताला और जाँच करते समय को खत्म करने की अनुमति होगी, और सिर्फ collection.GetConsumingEnumerable() पर एक foreach है। निर्माता लॉक को खत्म कर सकता है और आवश्यक वस्तुओं को जोड़ सकता है। यह आपको कई उत्पादकों का उपयोग करने के लिए आसानी से स्केल करने की अनुमति देगा, क्योंकि आपने बताया है कि इस समय आपके पास "बहुत भारी उत्पादक" है।

0

आपको लॉक करना चाहिए; कक्षा थ्रेडसेफ नहीं है। आप System.Collections में Queue उपयोग कर रहे हैं, वहाँ एक धागा सुरक्षित Queue काम (System.Collections.Queue.Synchronized() रिटर्न इस तरह के एक Queue) है।अन्यथा, Queue<T>.SyncRoot प्रदान की वस्तु का उपयोग करने के सिंक्रनाइज़ करने के लिए सुनिश्चित हो: यदि आप .NET 3.5 का उपयोग कर रहे हैं, भले ही

using System.Collections.Generic; 
public static class Q_Example 
{ 
    private readonly Queue<int> q = new Queue<int>(); 
    public void Method1(int val) 
    { 
     lock(q.SyncRoot) 
     { 
      q.EnQueue(val); 
     } 
    } 

    public int Method2() 
    { 
     lock(q.SyncRoot) 
     { 
      return q.Dequeue(); 
     } 
    } 
} 
+0

कतार क्यू के लिए क्यों पढ़ा गया है? सिस्टम के साथ – 5YrsLaterDBA

+0

.Collections.Queue.Synchronized(), हमें अब लॉकर की आवश्यकता नहीं है? – 5YrsLaterDBA

+0

1. क्योंकि संदर्भ क्यू अपने लक्ष्य को नहीं बदलेगा। केवल उस ऑब्जेक्ट/स्ट्रक्चर को मॉडिफ करने से रोकता है जो इसे इंगित करता है। 2. यह गैर सामान्य संग्रह देता है, इसलिए इसे .NET> 1.1 – dzendras

0

ConcurrentQueue उपलब्ध है। Reactive Extensions में .NET 3.5 के समानांतर एक्सटेंशन का उपयोग किया जाता है - कार्य समानांतर लाइब्रेरी के अग्रदूत जो .NET 4.0 में शामिल है।

1

नहीं, यह लगातार काम नहीं करेगा ... क्यों?

के दो तरीकों हम दो धागे से समवर्ती बुला जा रहे हैं एकत्रित न चलो (एक & एक लेखक पढ़ें):

public T Dequeue() 
{ 
    if (this._size == 0) 
    { 
     ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyQueue); 
    } 
    T local = this._array[this._head]; 
    this._array[this._head] = default(T); 
    this._head = (this._head + 1) % this._array.Length; 
    this._size--; 
    this._version++; 
    return local; 
} 

public void Enqueue(T item) 
{ 
    if (this._size == this._array.Length) 
    { 
     int capacity = (int) ((this._array.Length * 200L)/100L); 
     if (capacity < (this._array.Length + 4)) 
     { 
      capacity = this._array.Length + 4; 
     } 
     this.SetCapacity(capacity); 
    } 
    this._array[this._tail] = item; 
    this._tail = (this._tail + 1) % this._array.Length; 
    this._size++; 
    this._version++; 
} 

ऊपर कोड चर के तीन सुरक्षित हैं को देखते हुए अगर (और केवल यदि) कतार में पर्याप्त क्षमता है। _array, _head, और _tail के लिए फ़ील्ड्स या तो ऊपर दिए गए दो तरीकों में से एक में अनमोडिफाइड या संशोधित हैं।

कारण आप लॉक को हटा नहीं सकते हैं() यह है कि दोनों विधियां _size और _version को संशोधित करती हैं। हालांकि तर्कसंगत रूप से _version पर टकराव को अनदेखा किया जा सकता है, _size पर टकराव कुछ अवांछित और अप्रत्याशित व्यवहार का कारण बनता है।

+0

तो आपकी प्रोफ़ाइल के अनुसार यह कोड सार्वजनिक डोमेन है और कॉपीराइट से मुक्त है? ;) –

+0

@ चबैसिटी एलएमओओ - हाँ मुझे लगता है कि इसमें कुछ उल्लेखनीय अपवाद हैं;) अब जब आप इसे ऊपर लाए गए कोड को एसओ द्वारा लाइसेंस प्राप्त नहीं किया जा सकता है। मुझे आशा है कि एमएस हमारे मामूली विवेकाधिकारों को ध्यान में रखेगा क्योंकि हम सीएलआर/फ्रेमवर्क को समझने का प्रयास करते हैं और इसका सर्वोत्तम उपयोग कैसे करते हैं :) –

1

बीटीडब्ल्यू, एक पाठक के लिए लॉकलेस कतार और एक लेखक लिखने के लिए काफी आसान हैं। इस अवधारणा का एक बहुत ही आदिम उदाहरण है, लेकिन यह काम करता है:

class LocklessQueue<T> 
{ 
    class Item 
    { 
     public Item Next; 
     bool _valid; 
     T _value; 
     public Item(bool valid, T value) 
     { 
      _valid = valid; 
      _value = value; 
      Next = null; 
     } 
     public bool IsValid { get { return _valid; } } 
     public T TakeValue() 
     { 
      T value = _value; 
      _valid = false; 
      _value = default(T); 
      return value; 
     } 
    } 

    Item _first; 
    Item _last; 

    public LocklessQueue() 
    { 
     _first = _last = new Item(false, default(T)); 
    } 

    public bool IsEmpty 
    { 
     get 
     { 
      while (!_first.IsValid && _first.Next != null) 
       _first = _first.Next; 
      return false == _first.IsValid; 
     } 
    } 

    public void Enqueue(T value) 
    { 
     Item i = new Item(true, value); 
     _last.Next = i; 
     _last = i; 
    } 

    public T Dequeue() 
    { 
     while (!_first.IsValid && _first.Next != null) 
      _first = _first.Next; 

     if (IsEmpty) 
      throw new InvalidOperationException();//queue is empty 

     return _first.TakeValue(); 
    } 
} 
+0

तो आपके ऊपर लॉकलेसक्यूयू के लिए कोई लॉक की आवश्यकता नहीं है क्योंकि आप कभी भी स्थान को पुन: आवंटित नहीं करते हैं? – 5YrsLaterDBA

+0

यह एकल पाठक और एकल लेखक के लिए है, लेकिन हम इसे कई पाठकों और एकाधिक लेखकों के लिए उपयोग कर सकते हैं यदि हम एक समय में पढ़ने के लिए केवल एक पाठक बनाने के लिए अलग-अलग पाठकों और लेखकों को सिंक्रनाइज़ करते हैं और एक समय में केवल एक लेखक लिखते हैं। पाठकों और लेखकों के बीच कोई सिंक्रनाइज़ेशन की आवश्यकता नहीं है। सही? – 5YrsLaterDBA

+0

@ 5YrsLaterDBA सही है, लेकिन आप इसके बारे में मेरे ब्लॉग पर कुछ पोस्ट पढ़ सकते हैं: http://csharptest.net/?p=465 –

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