2012-03-29 39 views
8

मैं multithreads आवेदन राशि पर अमल नहीं हो सकता है और मैं इस त्रुटिसंग्रह संशोधित किया गया था, गणन आपरेशन

************** Exception Text ************** 
System.InvalidOperationException: Collection was modified; enumeration operation may not execute. 
    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) 
    at System.Collections.Generic.List`1.Enumerator.MoveNextRare() 
    at System.Collections.Generic.List`1.Enumerator.MoveNext() 
    ... 

मैं शायद अपने संग्रह के साथ समस्या है मिलता है, क्योंकि एक धागे पर मैं अपने संग्रह पढ़ सकते हैं और मैं को संशोधित एक और धागा पर संग्रह।

public readonly ObservableCollectionThreadSafe<GMapMarker> Markers = new ObservableCollectionThreadSafe<GMapMarker>(); 


public void problem() 
{ 
    foreach (GMapMarker m in Markers) 
    { 
    ... 
    } 
} 

मैं इस कोड के साथ संग्रह लॉक करने की कोशिश कर रहा हूं, लेकिन काम नहीं करता है।

public void problem() 
    { 
     lock(Markers) 
     { 
     foreach (GMapMarker m in Markers) 
     { 
      ... 
     } 
     } 
    } 

उस समस्या को ठीक करने के लिए कोई विचार?

+1

आप समस्या को 'foreach' अंदर कोड के साथ है, यह पोस्ट करें। – nemesv

+3

आप फोरैच – Reniuz

उत्तर

4

आपको पढ़ने और लेखन दोनों पक्षों को लॉक करने की आवश्यकता है। अन्यथा धागे में से एक ताला के बारे में पता नहीं है और, पढ़ने के लिए/संग्रह को संशोधित, जबकि अन्य को संशोधित/(क्रमशः) लॉक के साथ पढ़ रहा है आयोजित

4

कोशिश अपने संग्रह का क्लोन को पढ़ने के लिए कोशिश करेंगे

foreach (GMapMarker m in Markers.Copy()) 
{ 
    ... 
} 

इससे आपके संग्रह की एक नई प्रतिलिपि बनाई जाएगी जो किसी अन्य धागे से प्रभावित नहीं होगी लेकिन विशाल संग्रह के मामले में प्रदर्शन समस्या हो सकती है।

तो मुझे लगता है कि अगर आप प्रक्रियाओं को पढ़ने और लिखने के दौरान संग्रह को लॉक करते हैं तो यह बेहतर होगा।

+0

के साथ लूपिंग करते समय संग्रह को संशोधित नहीं कर सकते ... और मूल संग्रह को संशोधित करें। – Reniuz

+0

आप सही हैं, मुझे लगता है कि 'कॉपी' का उपयोग करना है, लेकिन इससे प्रदर्शन समस्या हो सकती है। –

8

यह सुंदर आम गलती है - foreach का उपयोग कर इसे पुनरावृत्ति जबकि एक संग्रह को संशोधित करने, कि foreach केवल पढ़ने के लिए IEnumerator उदाहरण का उपयोग करता है को ध्यान में रखना।

संग्रह अतिरिक्त सूचकांक चेक के साथ for() का उपयोग कर के माध्यम से लूप को आज़मा कर देखें तो अगर सूचकांक बाध्य से बाहर है - आप इस संभाल करने के लिए अतिरिक्त तर्क लागू करने में सक्षम हो जाएगा, यह भी पाश से बाहर निकलें शर्त के रूप में आप LINQ Count() उपयोग कर सकते हैं जो गिनती मूल्य का मूल्यांकन होगा हर बार अगर अंतर्निहित गणन ICollection को लागू नहीं करता है: -:

lock (Markers.SyncRoot) 

उपयोग for():

तो Markers औजार IColletion SyncRoot पर ताला

for (int index = 0; index < Markers.Count(); index++) 
{ 
    if (Markers>= Markers.Count()) 
    { 
     // TODO: handle this case to avoid run time exception 
    } 
} 

इस पोस्ट उपयोगी लग सकते: How do foreach loops work in C#?

+0

लेकिन अगर संशोधन संग्रह से किसी आइटम को हटाकर था, तो वह 'इंडेक्सऑटऑफेंजेंज' अपवाद –

+1

फेंक देगा, मैंने इस समस्या से बचने के लिए अतिरिक्त इंडेक्स चेक का उल्लेख किया है, नमूना जोड़ देगा, इस – sll

+0

पर इंगित करने के लिए धन्यवाद, मैं फ़ोरैच को प्रतिस्थापित करने के बारे में सोच रहा हूं लेकिन फिर मैंने सोचा कि अगर संग्रह को लॉक करना बेहतर होगा, लेकिन काम न करें:/ – PATO7

0

आप एक foreach उपयोग कर सकते हैं लेकिन आप एक सूची में संग्रह डाली और व्यवहार के तरीकों का उपयोग करने में डॉट ऑपरेटर का उपयोग करने के लिए है।

उदाहरण:। Markers.Tolist() foreach (i => i.DeleteObject())

पूरी तरह से सुनिश्चित नहीं हैं कि आप अपने संग्रह के साथ कर रहे हैं। मेरा उदाहरण यह मान रहा है कि आप संग्रह से सभी आइटम हटाना चाहते हैं, लेकिन इसे आपके संग्रह के साथ करने वाले किसी भी व्यवहार पर लागू किया जा सकता है।

0

मैं AsyncCommand का उपयोग करने का सुझाव दूंगा, क्योंकि AsyncCommand या तो लिया गया है या नहीं, lock(Markers) का उपयोग करते हुए पुनर्वितरण की अनुमति देता है।(https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock देखें):

private readonly AsyncLock _markersMutex = new AsyncLock(); 

    using (await _markersMutex.LockAsync().ConfigureAwait(false)) 
    { 
    foreach (GMapMarker m in Markers) 
    { 
     ... 
    } 
    } 

इसके अलावा, AsyncLock आप अपने async बराबर साथ Thread.Sleep को बदलने के लिए अनुमति देता है, await Task.Delay(TimeSpan.FromSeconds(1))

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

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