2010-12-29 11 views
9

के साथ सक्षम करने के लिए मेरे पिछले question है जो मैंने अपना समाधान प्रदान किया है; हालांकि, मेरे पास ConcurrentQueue<T> तक पहुंच नहीं है क्योंकि मैं नेट 3.5 पर हूं। समेकन की अनुमति देने के लिए मुझे Queue<T> की आवश्यकता है। मैंने यह question पढ़ा है और कतार में नहीं है और थ्रेडेड विधि किसी आइटम को निकालने का प्रयास करती है तो कोई समस्या उत्पन्न होती है।कंसूरेंसी <T> कंसुरेंसी

मेरा कार्य अब यह निर्धारित करना है कि मैं अपनी समवर्ती कतार कक्षा प्राप्त कर सकता हूं या नहीं।

public sealed class ConcurrentQueue : Queue<DataTable> 
{ 
    public event EventHandler<TableQueuedEventArgs> TableQueued; 
    private ICollection que; 

    new public void Enqueue(DataTable Table) 
    { 
     lock (que.SyncRoot) 
     { 
      base.Enqueue(Table); 
     } 

     OnTableQueued(new TableQueuedEventArgs(Dequeue())); 
    } 

    // this is where I think I will have a problem... 
    new public DataTable Dequeue() 
    { 
     DataTable table; 

     lock (que.SyncRoot) 
     { 
      table = base.Dequeue(); 
     } 

     return table; 
    } 

    public void OnTableQueued(TableQueuedEventArgs table) 
    { 
     EventHandler<TableQueuedEventArgs> handler = TableQueued; 

     if (handler != null) 
     { 
      handler(this, table); 
     } 
    } 
} 

तो, जब एक DataTable कतारबद्ध है, EventArgs एक dequeued तालिका घटना ग्राहक के लिए पारित करेंगे: यह है कि मैं क्या के साथ आया है। क्या यह कार्यान्वयन मुझे थ्रेड-सुरक्षित कतार प्रदान करेगा?

+3

'que' _utterly_ बेकार है। आपको 'रीडोनली ऑब्जेक्ट कुंजी = नई ऑब्जेक्ट();' पर लॉक करना चाहिए। – SLaks

+0

@ स्लक्स: मैंने एमएसडीएन पर आधारित 'आईसीओलेक्शन क्यू' और 'लॉक (que.SyncRoot)' लागू किया: http://msdn.microsoft.com/en-us/library/bb344892.aspx – IAbstract

+0

आपको इसकी आवश्यकता नहीं है सब। 'सिंक रूट' उपयोगी है यदि आपके पास कोड के टुकड़े अलग हैं जिन्हें एक ही संग्रह के लिए लॉक करने की आवश्यकता है। आपके मामले में, 'que' 'null' है। आपको बस अपनी विधियों में एक ही वस्तु को लॉक करने की आवश्यकता है। – SLaks

उत्तर

2

आप अपनी वस्तुओं को रद्द कर रहे हैं जैसे आप उन्हें घेरते हैं।
आपको अपने पैरामीटर का उपयोग करके ईवेंट को बढ़ाने की आवश्यकता है।

चाहे यह वास्तव में थ्रेड-सुरक्षित है, आप इसका उपयोग कैसे करते हैं इस पर निर्भर करता है।
यदि आपने कभी Count चेक किया है या खालीपन की जांच करें, तो यह थ्रेडसेफ नहीं है और आसानी से थ्रेडसेफ नहीं बनाया जा सकता है।
यदि आप नहीं करते हैं, तो आप शायद कतार की तुलना में कुछ सरल उपयोग कर सकते हैं।

+0

एनक्यूइंग को डेक्यूइंग से तेज होने की उम्मीद है (हालांकि, मेरे पास अभी तक किसी भी तरह की पुष्टि करने के लिए कोई डेटा नहीं है)। मुख्य धागे पर भी एनक्यूइंग किया जाता है। जब ईवेंट आग लगती है, तो मैं डीक्यूइंग प्रक्रिया को असीमित रूप से चलाने के लिए 'BeginInvoke'' क्रिया का उपयोग करता हूं। – IAbstract

+0

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

+0

ऐसा लगता है कि आपको कतार की आवश्यकता नहीं है; आप तालिका को 'BeginInvoke' के पैरामीटर के रूप में पास कर सकते हैं। – SLaks

3

मेरे पसंदीदा खोज इंजन की त्वरित यात्रा से पता चला कि मेरी याददाश्त सही थी; you can get the Task Parallel Library even on .NET 3.5System.Threading.dll वांछित प्राप्त करने के लिए The PFX team blog post on the subject और Reactive Extensions भी डाउनलोड करें जो आप डाउनलोड करते हैं।

+0

दुर्भाग्यवश, मैं नेट 3.5 में उपयोग की जाने वाली मूल पुस्तकालयों में (आंतरिक नीति द्वारा) बाध्य हूं - मूल रूप से सभी डेवलपर्स को उसी पुस्तकालयों का उपयोग करके रखते हुए। अगर मैं टीपीएलआईबी का उपयोग करने की कोशिश करता हूं, तो मुझे एक बदनाम के रूप में चिह्नित किया जाएगा। अन्यथा, एक अच्छा विकल्प। – IAbstract

+3

यह एक _insane_ नीति है। पहिया का पुन: आविष्कार सबसे खराब उत्पादकता नालियों में से एक है, और बग का एक बड़ा स्रोत है। – SLaks

+4

इस तरह की नीति आमतौर पर उन लोगों द्वारा लागू की जाती है जो प्रोग्रामिंग के बारे में कुछ भी नहीं जानते ... –

0

लाइन OnTableQueued(new TableQueuedEventArgs(Dequeue())); में अपने Enqueue विधि में

उपयोग Peek बजाय विपंक्ति

यह

OnTableQueued(new TableQueuedEventArgs(base.Peek()));

+0

यह थ्रेड-सुरक्षित नहीं है। आपके पास पैरामीटर है; ** इसका इस्तेमाल करें! ** – SLaks

3

सच है कि आप new उपयोग करने के लिए आधार से तरीकों को छिपाने के लिए की जरूरत होनी चाहिए कक्षा आमतौर पर एक संकेत है कि आपको विरासत के बजाय संरचना का उपयोग करना चाहिए ...

यहाँ एक सरल सिंक्रनाइज़ कतार है, जो विरासत का उपयोग नहीं करता है, लेकिन अभी भी मानक Queue<T> के व्यवहार पर निर्भर करता है:

public class ConcurrentQueue<T> : ICollection, IEnumerable<T> 
{ 
    private readonly Queue<T> _queue; 

    public ConcurrentQueue() 
    { 
     _queue = new Queue<T>(); 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     lock (SyncRoot) 
     { 
      foreach (var item in _queue) 
      { 
       yield return item; 
      } 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public void CopyTo(Array array, int index) 
    { 
     lock (SyncRoot) 
     { 
      ((ICollection)_queue).CopyTo(array, index); 
     } 
    } 

    public int Count 
    { 
     get 
     { 
      // Assumed to be atomic, so locking is unnecessary 
      return _queue.Count; 
     } 
    } 

    public object SyncRoot 
    { 
     get { return ((ICollection)_queue).SyncRoot; } 
    } 

    public bool IsSynchronized 
    { 
     get { return true; } 
    } 

    public void Enqueue(T item) 
    { 
     lock (SyncRoot) 
     { 
      _queue.Enqueue(item); 
     } 
    } 

    public T Dequeue() 
    { 
     lock(SyncRoot) 
     { 
      return _queue.Dequeue(); 
     } 
    } 

    public T Peek() 
    { 
     lock (SyncRoot) 
     { 
      return _queue.Peek(); 
     } 
    } 

    public void Clear() 
    { 
     lock (SyncRoot) 
     { 
      _queue.Clear(); 
     } 
    } 
} 
+0

स्पष्ट इंटरफ़ेस कार्यान्वयन और _queue पर डालने के लिए क्या? – abatishchev

+1

एक पुनरावर्तक में 'लॉक'-आईएनजी खतरनाक है। – SLaks

+1

इसके अलावा, यह अभी भी अधिकांश उपयोग-मामलों में थ्रेडसेफ नहीं है। – SLaks

2

प्रारंभिक प्रश्न के कुछ समय बाद, मुझे पता है (यह ऊपर के रूप में "संबद्ध" आया एक और सवाल के अधिकार में), लेकिन मैं इसी तरह के मामलों में निम्नलिखित के साथ चला गया है। सीपीयू-कैश-उपयोग के लिए उतना अच्छा नहीं जितना हो सकता है, लेकिन सरल, लॉक-फ्री, थ्रेड-सुरक्षित, और अक्सर सीपीयू-कैश-उपयोग इतना महत्वपूर्ण नहीं होता है कि ऑपरेशन के बीच अक्सर बड़े अंतर होते हैं, और कब आवंटन की निकटता प्रभाव को कम नहीं कर सकती:

internal sealed class LockFreeQueue<T> 
{ 
    private sealed class Node 
    { 
    public readonly T Item; 
    public Node Next; 
    public Node(T item) 
    { 
     Item = item; 
    } 
    } 
    private volatile Node _head; 
    private volatile Node _tail; 
    public LockFreeQueue() 
    { 
    _head = _tail = new Node(default(T)); 
    } 
#pragma warning disable 420 // volatile semantics not lost as only by-ref calls are interlocked 
    public void Enqueue(T item) 
    { 
    Node newNode = new Node(item); 
    for(;;) 
    { 
     Node curTail = _tail; 
     if (Interlocked.CompareExchange(ref curTail.Next, newNode, null) == null) //append to the tail if it is indeed the tail. 
     { 
     Interlocked.CompareExchange(ref _tail, newNode, curTail); //CAS in case we were assisted by an obstructed thread. 
     return; 
     } 
     else 
     { 
     Interlocked.CompareExchange(ref _tail, curTail.Next, curTail); //assist obstructing thread. 
     } 
    } 
    }  
    public bool TryDequeue(out T item) 
    { 
    for(;;) 
    { 
     Node curHead = _head; 
     Node curTail = _tail; 
     Node curHeadNext = curHead.Next; 
     if (curHead == curTail) 
     { 
     if (curHeadNext == null) 
     { 
      item = default(T); 
      return false; 
     } 
     else 
      Interlocked.CompareExchange(ref _tail, curHeadNext, curTail); // assist obstructing thread 
     } 
     else 
     { 
     item = curHeadNext.Item; 
     if (Interlocked.CompareExchange(ref _head, curHeadNext, curHead) == curHead) 
     { 
      return true; 
     } 
     } 
    } 
    } 
#pragma warning restore 420 
}