2009-07-02 17 views
8

मेरे पास सी # में एक स्थिति है जहां मेरे पास सरल प्रकार की एक सूची है। इस सूची को एकाधिक धागे से एक्सेस किया जा सकता है: प्रविष्टियों को जोड़ा या हटाया जा सकता है, और एक प्रविष्टि का अस्तित्व चेक किया जा सकता है। मैंने अभी तक उन तीन परिचालनों को उजागर करने वाली किसी ऑब्जेक्ट में सूची को समाहित किया है।सी # समवर्ती सूची प्रश्न

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

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

ये सरल नौसिखिया परिस्थितियों की तरह लग सकते हैं लेकिन मैं खुद को समेकन के मुद्दों पर ताज़ा कर रहा हूं और यह मुझे थोड़ा पागल बना रहा है, और मैं सी # की समवर्ती तंत्र से भी परिचित नहीं हूं।

इसे संभालने का सबसे अच्छा तरीका क्या होगा? क्या मैं पूरी तरह से बंद हूँ? जांच और जोड़ना चाहिए (परीक्षण और सेट?) चौथे परमाणु ऑपरेशन में संयुक्त किया जाना चाहिए? क्या मैं बस अपनी विधियों में लॉक ब्लॉक जोड़ रहा हूं जहां सूची का उपयोग किया जाता है?

इसके अलावा, क्या इस तरह की चीज का परीक्षण करना संभव है (सरल संचालन, समवर्ती परिस्थितियों में नहीं)?

उत्तर

8

यूनिट परीक्षण निश्चित रूप से कठिन होगा।

यह सभी .NET में "मूल" समरूपता तंत्र के साथ उचित रूप से किया जा सकता है: लॉक स्टेटमेंट और Monitor.Wait/Monitor.PulseAll। जब तक आपके पास प्रति आइटम एक अलग मॉनीटर न हो, तब भी जब भी कुछ भी हटा दिया जाता है, तो आपको सभी धागे को उठाने की आवश्यकता होगी - अन्यथा आप जागने के लिए "दाएं" धागे को बताने में सक्षम नहीं होंगे।

यदि यह वास्तव में सिर्फ एक सेट मदों की, आप, List<T> के बजाय HashSet<T> उपयोग करने के लिए जिस तरह से संग्रह का प्रतिनिधित्व करने के लिए चाहते हो सकता - कुछ भी नहीं आप उल्लेख किया है आदेश के साथ क्या करना है।

नमूना कोड, यह सोचते हैं कि एक सेट आप के लिए ठीक है:

using System; 
using System.Collections.Generic; 
using System.Threading; 

public class LockCollection<T> 
{ 
    private readonly HashSet<T> items = new HashSet<T>(); 
    private readonly object padlock = new object(); 

    public bool Contains(T item) 
    { 
     lock (padlock) 
     { 
      return items.Contains(item); 
     } 
    } 

    public bool Add(T item) 
    { 
     lock (padlock) 
     { 
      // HashSet<T>.Add does what you want already :) 
      // Note that it will return true if the item 
      // *was* added (i.e. !Contains(item)) 
      return items.Add(item); 
     } 
    } 

    public void WaitForNonExistence(T item) 
    { 
     lock (padlock) 
     { 
      while (items.Contains(item)) 
      { 
       Monitor.Wait(padlock); 
      } 
     } 
    } 

    public void WaitForAndAdd(T item) 
    { 
     lock (padlock) 
     { 
      WaitForNonExistence(item); 
      items.Add(item); 
     } 
    } 

    public void Remove(T item) 
    { 
     lock (padlock) 
     { 
      if (items.Remove(item)) 
      { 
       Monitor.PulseAll(padlock); 
      } 
     } 
    } 
} 

(। पूरी तरह से अपरीक्षित, बेशक तुम भी प्रतीक्षा कोड के लिए समय समाप्ति निर्दिष्ट करने के लिए चाहते हो सकता है ...)

+0

+1 है, मैं एक ऐसी ही समाधान के लिए आया था, लेकिन HashSet इसे और अधिक कुशल बनाता है। नेस्टेड ताले आदि का उपयोग करते हुए मॉनीटर क्लास के लिए अच्छा डेमो इत्यादि। जब कोई आइटम हटा दिया जाता है तो समस्या केवल 'स्टैम्पडे' हो सकती है। –

8

जबकि # 1 लिखने के लिए सबसे सरल हो सकता है, यह अनिवार्य रूप से एक बेकार विधि है। जब तक आप "एक प्रविष्टि के अस्तित्व" के लिए कोई प्रश्न समाप्त करने के बाद एक ही लॉक पर नहीं हो रहे हैं, तो आप वास्तव में "अतीत में किसी बिंदु पर एक प्रविष्टि का अस्तित्व" लौट रहे हैं। यह आपको प्रविष्टि के वर्तमान अस्तित्व के बारे में कोई जानकारी नहीं देता है।

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

समवर्ती सूची पर संचालन शामिल है उस ऑपरेशन के साथ संयुक्त होना चाहिए जिसे आप उस चेक के सही/झूठे अस्तित्व के मामले में करने की योजना बना रहे हैं।उदाहरण के लिए TestAdd() या TestRemove() अधिक से अधिक सुरक्षित है + जोड़ें या इसमें शामिल हैं +

+0

आपको थ्रेड सुरक्षित संग्रह इंटरफेस पर अपने ब्लॉग पोस्ट से लिंक करना चाहिए। –

+0

हां, लेकिन # 4 बस यही करता है। अन्य तरीकों की उपयोगिता संदिग्ध है, लेकिन जरूरी नहीं कि गलत है। –

+0

@ हेनक, मुझे लगता है कि # 2- # 4 अच्छे तरीके हैं। # 1 हालांकि गलत नहीं है लेकिन यह सिर्फ उपयोगी नहीं है। – JaredPar

1

दौड़ की स्थिति खोजने और यूनिट परीक्षणों जैसे कि एक उत्पाद है। इसे TypeMock Racer कहा जाता है। हालांकि, मैं इसके प्रभावशीलता के लिए या इसके खिलाफ कुछ भी नहीं कह सकता। :)

2

यहाँ एक उचित, समवर्ती, धागा सुरक्षित, parallelisable समवर्ती सूची कार्यान्वयन http://www.deanchalk.me.uk/post/Task-Parallel-Concurrent-List-Implementation.aspx

+1

नहीं, यह कार्यान्वयन पूरी तरह गैर-थ्रेड सुरक्षित है और अत्यधिक लॉकिंग इसे खराब विकल्प बनाता है। –

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