2012-04-04 8 views
7

मैं foreach के साथ तत्वों की एक सूची के माध्यम से पाशन कर रहा हूँ इस तरह:,एक और धागे से सूची संशोधित करते, पुनरावृत्ति (सी #)

foreach (Type name in aList) { 
    name.doSomething(); 
} 

हालांकि एक अन्य सूत्र में मैं

aList.Remove(Element); 
की तरह कुछ बोल रहा हूँ

रनटाइम के दौरान, यह एक अवैधऑपरेशन अपवाद का कारण बनता है: संग्रह संशोधित किया गया था; गणना ऑपरेशन निष्पादित नहीं हो सकता है। इसे संभालने का सबसे अच्छा तरीका क्या है (मैं प्रदर्शन की लागत पर भी इसे सरल मानता हूं)?

धन्यवाद!

उत्तर

7

थ्रेड एक:

lock (aList) { 
    foreach (Type name in aList) { 
    name.doSomething(); 
    } 
} 

थ्रेड बी:

lock (aList) { 
    aList.Remove(Element); 
} 

यह बिल्कुल प्रदर्शन के लिए वास्तव में बुरा है।

+0

धन्यवाद, एक आकर्षण की तरह काम करता :) चलो आशा है कि मैं का उपयोग कर किसी भी प्रदर्शन की समस्याओं नहीं मिलेगा .. –

9

सबसे अच्छा तरीका यह (मैं इसे perfer होगा भी प्रदर्शन की कीमत पर नहीं बल्कि सरल होने के लिए) को संभालने के लिए क्या है?

मूल रूप से: लॉक किए बिना एकाधिक धागे से गैर-थ्रेड-सुरक्षित संग्रह को संशोधित करने का प्रयास न करें। तथ्य यह है कि आप पुनरावृत्त कर रहे हैं, यहां अधिकतर अप्रासंगिक है - यह आपको इसे तेज़ी से ढूंढने में मदद करता है। यह एक ही समय में Remove दोनों को कॉल करने के लिए दो धागे के लिए असुरक्षित होता।

या तो एक धागा सुरक्षित संग्रह जैसे ConcurrentBagया सुनिश्चित करें कि केवल एक धागा एक समय में संग्रह के साथ कुछ भी करता बनाने का उपयोग करें।

+0

कि के बारे में सोचना नहीं था, लेकिन अब आप ने कहा कि है कि यह समझ में आता है। धन्यवाद, मैं शायद बाद में इसका उपयोग करूँगा, लेकिन सिर्फ इस मामले के लिए लॉक ठीक काम करता है। –

0

अगर आप सिर्फ बचना चाहते हैं अपवाद का उपयोग

foreach (Type name in aList.ToArray()) 
{ name.doSomething(); } 

बारे में पता होना DoSomething() मामले में भी क्रियान्वित किया जाता है तत्व अन्य सूत्र में हटा दिया गया था

+1

दुर्भाग्यवश 'ToArray' और' निकालें 'संभवतः एक अलग तरह का अपवाद उत्पन्न कर सकता है। हालांकि यह एक अच्छा विचार है! यह देखने के लिए कि यह सही तरीके से काम करने के तरीके को देखने के लिए मेरे [उत्तर] (http://stackoverflow.com/a/10018399/158779) का संदर्भ लें। –

+0

ओह ... और स्टैक ओवरफ्लो में आपका स्वागत है :) –

0

मैं नहीं कर सकता विशेष रूप से अपने प्रश्न से बताएं, लेकिन (ऐसा लगता है) आप प्रत्येक आइटम पर एक क्रिया कर रहे हैं और फिर इसे हटा रहे हैं। आप BlockingCollection<T> पर देखना चाहते हैं, जिसमें यह देखने के लिए GetConsumingEnumerable() पर कॉल करने का एक तरीका है, यह देखने के लिए कि यह आपके लिए उपयुक्त है या नहीं। यहां एक छोटा सा नमूना है। http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim.aspx

, बस एक ताला आप सिर्फ एक पाठक है:

void SomeMethod() 
{ 
    BlockingCollection<int> col = new BlockingCollection<int>(); 

    Task.StartNew(() => { 

     for (int j = 0; j < 50; j++) 
     { 
      col.Add(j); 
     } 

     col.CompleteAdding(); 

    }); 

    foreach (var item in col.GetConsumingEnumerable()) 
    { 
     //item is removed from the collection here, do something 
     Console.WriteLine(item); 
    } 
} 
1

आप एक पाठक से अधिक है, तो एक रीडर-राइटर लॉक (नेट 3.5+), स्लिम कोशिश सूची में स्वयं या एक निजी वस्तु (लेकिन स्वयं को प्रकार पर लॉक न करें), जैसा कि यूजीन रिक के जवाब में दिखाया गया है।

9

विधि # 1:

सरल और कम से कम प्रभावी तरीका पाठकों और लेखकों के लिए एक महत्वपूर्ण अनुभाग बनाने के लिए है।

// Writer 
lock (aList) 
{ 
    aList.Remove(item); 
} 

// Reader 
lock (aList) 
{ 
    foreach (T name in aList) 
    { 
    name.doSomething(); 
    } 
} 

विधि # 2:

यह # 1 विधि के समान है, लेकिन foreach पाश की पूरी अवधि के लॉक रोक के बजाय आप संग्रह पहले कॉपी और फिर से अधिक पुनरावृति प्रति।

// Writer 
lock (aList) 
{ 
    aList.Remove(item); 
} 

// Reader 
List<T> copy; 
lock (aList) 
{ 
    copy = new List<T>(aList); 
} 
foreach (T name in copy) 
{ 
    name.doSomething(); 
} 

विधि # 3:

यह सब अपने विशिष्ट स्थिति पर निर्भर करता है, लेकिन जिस तरह से मैं सामान्य रूप से इस समस्या से निपटने संग्रह अपरिवर्तनीय करने के लिए मास्टर संदर्भ रखने के लिए है। इस तरह आपको पाठक पक्ष पर पहुंच को सिंक्रनाइज़ नहीं करना पड़ता है। चीजों के लेखक पक्ष को lock की आवश्यकता है। पाठक पक्ष को कुछ भी नहीं चाहिए जिसका अर्थ है कि पाठक अत्यधिक समवर्ती रहते हैं। आपको केवल एक चीज करने की आवश्यकता है aList संदर्भ volatile के रूप में संदर्भित करें।

// Variable declaration 
object lockref = new object(); 
volatile List<T> aList = new List<T>(); 

// Writer 
lock (lockref) 
{ 
    var copy = new List<T>(aList); 
    copy.Remove(item); 
    aList = copy; 
} 

// Reader 
List<T> local = aList; 
foreach (T name in local) 
{ 
    name.doSomething(); 
} 
+0

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

+0

@DerekW: हाँ, यह एक अच्छा अभ्यास है और अच्छी तरह से ध्यान दिया गया है। हकीकत में वास्तव में डेडलॉक का कारण बनने की संभावना नहीं है क्योंकि ऐसी घटनाओं (नेस्टेड ताले आदि) के लिए सामान्य परिदृश्य यहां नहीं हैं। तो जब तक कि कोड का एक और टुकड़ा पूरी तरह से 'एलीस्ट' का दुरुपयोग नहीं करता है, तो सबसे खराब होने की वजह से लॉक विवाद बढ़ जाता है। मुझे लगता है कि पूरी 'लॉक (यह)' बहस थोड़ी अधिक उछाल आई है और कुछ लोग स्वीकार करेंगे कि पैरानिया पक्ष की ओर थोड़ा बहुत दूर है। फिर भी, हालांकि इस तरह के मुहावरे से बचने के लिए अभी भी अच्छा अभ्यास है। –

+0

मुझे यह भी इंगित करना चाहिए कि मैं इसके बारे में सोच रहा हूं कि # 3 को * बिल्कुल * प्रस्तुत किया जाना चाहिए। कोई विचलन (यह तय करने की तरह कि 'स्थानीय' को असाइनमेंट किया जा सकता है) संभावित रूप से यादृच्छिक और शानदार असफलताओं का कारण बन जाएगा। मुझे पता है कि यह सब कुछ के लिए स्पेसटाइम में भी पूरी तरह से चीर सकता है। –