संग्रह को संशोधित किया गया था; गणना ऑपरेशन निष्पादित नहीं हो सकता है


908

मैं इस त्रुटि की तह तक नहीं जा सकता, क्योंकि जब डिबगर संलग्न होता है, तो ऐसा प्रतीत नहीं होता है। नीचे कोड है।

यह एक Windows सेवा में WCF सर्वर है। जब भी कोई डेटा इवेंट (यादृच्छिक अंतराल पर, लेकिन बहुत बार नहीं - प्रति दिन लगभग 800 बार) सेवा द्वारा NotifySubscribers को कॉल किया जाता है।

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

क्या आपको इस कोड में कोई समस्या दिखाई देती है? क्या मुझे शब्दकोश थ्रेड को सुरक्षित बनाने की आवश्यकता है?

[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class SubscriptionServer : ISubscriptionServer
{
    private static IDictionary<Guid, Subscriber> subscribers;

    public SubscriptionServer()
    {            
        subscribers = new Dictionary<Guid, Subscriber>();
    }

    public void NotifySubscribers(DataRecord sr)
    {
        foreach(Subscriber s in subscribers.Values)
        {
            try
            {
                s.Callback.SignalData(sr);
            }
            catch (Exception e)
            {
                DCS.WriteToApplicationLog(e.Message, 
                  System.Diagnostics.EventLogEntryType.Error);

                UnsubscribeEvent(s.ClientId);
            }
        }
    }


    public Guid SubscribeEvent(string clientDescription)
    {
        Subscriber subscriber = new Subscriber();
        subscriber.Callback = OperationContext.Current.
                GetCallbackChannel<IDCSCallback>();

        subscribers.Add(subscriber.ClientId, subscriber);

        return subscriber.ClientId;
    }


    public void UnsubscribeEvent(Guid clientId)
    {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                    e.Message);
        }
    }
}

मेरे मामले में यह एक संपार्श्विक प्रभाव था क्योंकि मैं कुछ का उपयोग कर रहा था। Include ("तालिका") जो प्रक्रिया के दौरान संशोधित हुई - कोड पढ़ते समय बहुत स्पष्ट नहीं। हालाँकि, मैं भाग्यशाली था कि इसमें शामिल लोगों की आवश्यकता नहीं थी (हाँ! पुराना, अचिन्त्य कोड) और मैंने सिर्फ उन्हें हटाकर अपना मुद्दा हल कर लिया
आदि

जवाबों:


1630

क्या होने की संभावना है कि सिग्नलडाटा अप्रत्यक्ष रूप से ग्राहकों के शब्दकोश को लूप के दौरान हुड के नीचे बदल रहा है और उस संदेश के लिए अग्रणी है। आप इसे बदलकर सत्यापित कर सकते हैं

foreach(Subscriber s in subscribers.Values)

सेवा

foreach(Subscriber s in subscribers.Values.ToList())

अगर मैं सही हूं, तो समस्या दूर हो जाएगी

कॉलिंग की शुरुआत में एक अलग सूची subscribers.Values.ToList()के मूल्यों को कॉपी करता subscribers.Valuesहै foreach। इस सूची में और कुछ भी नहीं है (यह एक चर नाम भी नहीं है!), इसलिए कुछ भी इसे लूप के अंदर संशोधित नहीं कर सकता है।


14
BTW .ToList () System.Core dll में मौजूद है जो .NET 2.0 अनुप्रयोगों के साथ संगत नहीं है। इसलिए आपको अपने लक्ष्य ऐप को .Net 3.5
mishal153

60
मुझे समझ नहीं आता क्यों एक ToList किया था और यही कारण है कि ठीक करता है सब कुछ
PositiveGuy

204
@ कॉफ़ी एडिक्ट: मुद्दा यह है कि लूप के subscribers.Valuesअंदर संशोधित किया जा रहा है foreach। कॉलिंग की शुरुआत में एक अलग सूची subscribers.Values.ToList()के मूल्यों को कॉपी करता subscribers.Valuesहै foreach। इस सूची में और कुछ भी नहीं है (यह एक चर नाम भी नहीं है!) , इसलिए कुछ भी इसे लूप के अंदर संशोधित नहीं कर सकता है।
ब्लूराजा - डैनी पफ्लुगुफ्ट

31
ध्यान दें कि ToListयदि ToListनिष्पादन करते समय संग्रह को संशोधित किया गया था, तब भी फेंक सकते हैं ।
श्रीराम सक्तीवेल

16
मुझे पूरा यकीन है कि लगता है कि इस मुद्दे को ठीक नहीं करता है, लेकिन यह केवल पुन: पेश करने के लिए कठिन बना देता है। ToListपरमाणु ऑपरेशन नहीं है। यहां तक ​​कि मजेदार भी है, ToListआधारभूत foreachरूप से एक नई सूची उदाहरण में वस्तुओं की प्रतिलिपि बनाने के लिए आंतरिक रूप से करता है , जिसका अर्थ है कि आपने foreachएक अतिरिक्त (हालांकि तेज) foreachचलना जोड़कर एक समस्या तय की है ।
ग्रू

113

जब कोई ग्राहक सदस्यता छोड़ता है तो आप गणना के दौरान सबस्क्राइबर्स के संग्रह की सामग्री को बदल रहे हैं।

इसे ठीक करने के कई तरीके हैं, एक स्पष्ट उपयोग करने के लिए लूप को बदलने के लिए .ToList():

public void NotifySubscribers(DataRecord sr)  
{
    foreach(Subscriber s in subscribers.Values.ToList())
    {
                                              ^^^^^^^^^  
        ...

64

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

private List<Guid> toBeRemoved = new List<Guid>();

फिर आप इसे बदल दें:

public void NotifySubscribers(DataRecord sr)
{
    toBeRemoved.Clear();

    ...your unchanged code skipped...

   foreach ( Guid clientId in toBeRemoved )
   {
        try
        {
            subscribers.Remove(clientId);
        }
        catch(Exception e)
        {
            System.Diagnostics.Debug.WriteLine("Unsubscribe Error " + 
                e.Message);
        }
   }
}

...your unchanged code skipped...

public void UnsubscribeEvent(Guid clientId)
{
    toBeRemoved.Add( clientId );
}

यह न केवल आपकी समस्या का समाधान करेगा, यह आपको अपने शब्दकोश से एक सूची बनाते रहने से रोकेगा, जो कि वहाँ बहुत सारे ग्राहक होने पर महंगा है। किसी दिए गए पुनरावृत्ति पर हटाए जाने वाले ग्राहकों की सूची को मानकर सूची में कुल संख्या से कम है, यह तेज होना चाहिए। लेकिन निश्चित रूप से यह प्रोफ़ाइल करने के लिए स्वतंत्र महसूस करें यह सुनिश्चित करें कि यदि आपके विशिष्ट उपयोग की स्थिति में कोई संदेह है तो यह मामला है।


9
यदि आप एक बड़े संग्रह के साथ काम कर रहे हैं, तो मैं इस पर विचार करने के लायक होने की उम्मीद कर रहा हूं। अगर यह छोटा होता तो मैं शायद सिर्फ और सिर्फ आगे बढ़ता।
कार्ल कीनिंगर

42

जब भी आप इसे संशोधित किया जा रहा है, तो इसे संशोधित करने से रोकने के लिए आप अपने सब्सक्राइबर शब्दकोश को भी लॉक कर सकते हैं:

 lock (subscribers)
 {
         foreach (var subscriber in subscribers)
         {
               //do something
         }
 }

2
क्या यह एक पूर्ण उदाहरण है? मेरे पास एक वर्ग है (_dEDIA obj नीचे) जिसमें एक सामान्य शब्दकोश शामिल है <string, int> जिसका नाम MarkerFrequencies है, लेकिन ऐसा करने से तुरंत क्रैश हल नहीं हुआ: लॉक (_dEDIA.MarkerFrequencies) {foreach (KeyValuePair <string, int> pair in _dEDIA.MarkerFrequencies) {...}}
जॉन Coombs

4
@JCbs यह संभव है कि आप संशोधित कर रहे हैं, संभवत: MarkerFrequenciesलॉक के अंदर ही शब्दकोश को पुन: असाइन कर रहे हैं , जिसका अर्थ है कि मूल उदाहरण अब लॉक नहीं है। इसके forबजाय a का उपयोग करके foreachदेखें, यह और यह देखें । मुझे पता है कि अगर यह हल करती है।
मोहम्मद सिपाहीवंड

1
खैर, मैं अंत में इस बग को ठीक करने के लिए चारों ओर हो गया, और ये सुझाव उपयोगी थे - धन्यवाद! मेरा डेटासेट छोटा था, और यह ऑपरेशन सिर्फ एक यूआई डिस्प्ले अपडेट था, इसलिए एक कॉपी पर पुनरावृत्ति करना सबसे अच्छा लग रहा था। (मैंने दोनों थ्रेड्स में MarkerFrequencies को लॉक करने के बाद foreach के बजाय प्रयोग करने के साथ प्रयोग किया। इस दुर्घटना को रोका गया लेकिन ऐसा लगता है कि आगे डिबगिंग कार्य करने की आवश्यकता है। और इसने और अधिक जटिलता पेश की। यहां दो थ्रेड होने का एकमात्र कारण उपयोगकर्ता को रद्द करने के लिए सक्षम करना है। एक ऑपरेशन, जिस तरह से। यूआई सीधे उस डेटा को संशोधित नहीं करता है।)
जॉन Coombs

9
समस्या यह है कि बड़े अनुप्रयोगों के लिए, ताले एक प्रमुख प्रदर्शन हिट हो सकते हैं - System.Collections.Concurrentनाम स्थान में एक संग्रह का उपयोग करने के लिए बेहतर है ।
BrainSlugs83

28

यह त्रुटि क्यों?

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

इसका एक उपाय है

अगर हम इसकी कुंजियों की सूची का उपयोग करते हुए एक शब्दकोश को टाइप करते हैं, तो समानांतर में हम शब्दकोश ऑब्जेक्ट को संशोधित कर सकते हैं, जैसा कि हम कुंजी-संग्रह के माध्यम से पुनरावृत्ति कर रहे हैं और शब्दकोश (और इसके प्रमुख संग्रह को पुनरावृत्ति) नहीं कर रहे हैं।

उदाहरण

//get key collection from dictionary into a list to loop through
List<int> keys = new List<int>(Dictionary.Keys);

// iterating key collection using a simple for-each loop
foreach (int key in keys)
{
  // Now we can perform any modification with values of the dictionary.
  Dictionary[key] = Dictionary[key] - 1;
}

यहाँ इस समाधान के बारे में एक ब्लॉग पोस्ट है

और StackOverflow में एक गहरी गोता लगाने के लिए: यह त्रुटि क्यों होती है?


धन्यवाद! ऐसा क्यों होता है इसकी स्पष्ट व्याख्या और मुझे तुरंत अपने आवेदन में इसे हल करने का तरीका दिया।
किम क्रॉसर

5

दरअसल समस्या मुझे यह लगती है कि आप सूची से तत्वों को हटा रहे हैं और सूची को पढ़ने के लिए जारी रखने की अपेक्षा कर रहे हैं जैसे कि कुछ भी नहीं हुआ था।

आपको वास्तव में क्या करने की आवश्यकता है जो अंत से शुरू होकर वापस भीख मांगने तक है। यहां तक ​​कि अगर आप सूची से तत्वों को हटाते हैं तो भी आप इसे पढ़ना जारी रख पाएंगे।


मैं नहीं देखता कि कैसे कोई फर्क पड़ेगा? यदि आप सूची के बीच में किसी तत्व को हटा रहे हैं, तो यह अभी भी एक त्रुटि फेंक देगा क्योंकि जिस आइटम तक पहुंचने की कोशिश कर रहा है वह नहीं मिल सकता है?
Zapnologica

4
@Zapnologica अंतर यह है - आप सूची के लिए गणना नहीं कर रहे हैं - प्रत्येक के लिए एक करने के बजाय, आप एक के लिए कर रहे हैं / अगले और पूर्णांक द्वारा इसे एक्सेस कर रहे हैं - आप निश्चित रूप से एक सूची में एक संशोधित कर सकते हैं के लिए / अगले लूप, लेकिन कभी नहीं के लिए / प्रत्येक लूप (क्योंकि / प्रत्येक प्रगणकों के लिए ) - आप इसे फॉरवर्ड / फॉरवर्ड में भी कर सकते हैं, बशर्ते आपके पास अपने काउंटरों को समायोजित करने के लिए अतिरिक्त तर्क हों, आदि
BrainSlugs83

4

InvalidOperationException- एक InvalidOperationException हुई है। यह एक "संग्रह को संशोधित किया गया था" एक फॉर्च-लूप में रिपोर्ट करता है

एक बार जब ऑब्जेक्ट हटा दिया जाता है, तो ब्रेक स्टेटमेंट का उपयोग करें।

उदाहरण के लिए:

ArrayList list = new ArrayList(); 

foreach (var item in list)
{
    if(condition)
    {
        list.remove(item);
        break;
    }
}

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

3

मेरे पास एक ही मुद्दा था, और इसे हल किया गया था जब मैंने forइसके बजाय एक लूप का उपयोग किया था foreach

// foreach (var item in itemsToBeLast)
for (int i = 0; i < itemsToBeLast.Count; i++)
{
    var matchingItem = itemsToBeLast.FirstOrDefault(item => item.Detach);

   if (matchingItem != null)
   {
      itemsToBeLast.Remove(matchingItem);
      continue;
   }
   allItems.Add(itemsToBeLast[i]);// (attachDetachItem);
}

11
यह कोड गलत है और संग्रह में कुछ वस्तुओं को छोड़ देगा यदि कोई तत्व हटा दिया जाएगा। उदाहरण के लिए: आपके पास var arr = ["a", "b", "c"] है और प्रथम पुनरावृत्ति (i = 0) में आप स्थिति 0 (तत्व "a") पर तत्व निकालते हैं। इसके बाद सभी सरणी तत्व एक स्थिति को ऊपर ले जाएंगे और सरणी ["बी", "सी"] होगी। तो, अगले पुनरावृत्ति (i = 1) में आप स्थिति 1 पर तत्व की जांच करेंगे जो "सी" नहीं "बी" होगा। ये गलत है। इसे ठीक करने के लिए, आपको नीचे से ऊपर की ओर जाना होगा
Kaspars Ozols

3

ठीक है तो इससे मुझे जो मदद मिली वह थी पीछे की ओर चलना। मैं एक सूची से एक प्रविष्टि को हटाने की कोशिश कर रहा था, लेकिन ऊपर की ओर घूम रहा था और इसने लूप को खराब कर दिया क्योंकि प्रविष्टि अब मौजूद नहीं थी:

for (int x = myList.Count - 1; x > -1; x--)
                        {

                            myList.RemoveAt(x);

                        }

2

मैंने इसके लिए कई विकल्प देखे हैं लेकिन मेरे लिए यह सबसे अच्छा था।

ListItemCollection collection = new ListItemCollection();
        foreach (ListItem item in ListBox1.Items)
        {
            if (item.Selected)
                collection.Add(item);
        }

तो बस संग्रह के माध्यम से पाश।

ध्यान रखें कि एक ListItemCollection में डुप्लिकेट हो सकते हैं। डिफ़ॉल्ट रूप से डुप्लिकेट को संग्रह में जोड़े जाने से रोकने के लिए कुछ भी नहीं है। डुप्लिकेट से बचने के लिए आप ऐसा कर सकते हैं:

ListItemCollection collection = new ListItemCollection();
            foreach (ListItem item in ListBox1.Items)
            {
                if (item.Selected && !collection.Contains(item))
                    collection.Add(item);
            }

यह कोड डुप्लिकेट को डेटाबेस में दर्ज होने से कैसे रोकेगा। मैंने कुछ ऐसा ही लिखा है और जब मैं सूची बॉक्स से नए उपयोगकर्ता जोड़ता हूं, अगर मैं गलती से एक चयनित रखता हूं जो पहले से ही सूची में है, तो यह एक डुप्लिकेट प्रविष्टि बनाएगा। क्या आपके पास कोई सुझाव है @ मायके?
जेमी

1

सबसे खराब स्थिति में स्वीकृत उत्तर गलत और गलत है। यदि परिवर्तन किए जाते हैंToList() , तो आप अभी भी एक त्रुटि के साथ समाप्त हो सकते हैं। इसके अलावा lock, यदि आपके पास कोई सार्वजनिक सदस्य है, तो प्रदर्शन और थ्रेड-सेफ्टी को ध्यान में रखा जाना चाहिए, एक उचित समाधान अपरिवर्तनीय प्रकारों का उपयोग कर सकता है ।

सामान्य तौर पर, एक अपरिवर्तनीय प्रकार का मतलब है कि आप एक बार बनाए जाने के बाद इसकी स्थिति को बदल नहीं सकते हैं। तो आपका कोड इस तरह दिखना चाहिए:

public class SubscriptionServer : ISubscriptionServer
{
    private static ImmutableDictionary<Guid, Subscriber> subscribers = ImmutableDictionary<Guid, Subscriber>.Empty;
    public void SubscribeEvent(string id)
    {
        subscribers = subscribers.Add(Guid.NewGuid(), new Subscriber());
    }
    public void NotifyEvent()
    {
        foreach(var sub in subscribers.Values)
        {
            //.....This is always safe
        }
    }
    //.........
}

यदि आप एक सार्वजनिक सदस्य हैं तो यह विशेष रूप से उपयोगी हो सकता है। अन्य कक्षाएं हमेशा foreachसंग्रह के बारे में चिंता किए बिना अपरिवर्तनीय प्रकारों पर संशोधित की जा सकती हैं।


1

यहां एक विशिष्ट परिदृश्य है जो एक विशेष दृष्टिकोण को वारंट करता है:

  1. Dictionaryअक्सर प्रगणित है।
  2. Dictionaryबार बार संशोधित किया गया है।

इस परिदृश्य में प्रत्येक गणना से पहले Dictionary(या Dictionary.Values) की एक प्रति बनाना काफी महंगा हो सकता है। इस समस्या को हल करने के बारे में मेरा विचार एक से अधिक प्रतिरूपों में एक ही कैश्ड कॉपी का पुन: उपयोग करना है, और अपवादों के लिए IEnumeratorमूल का एक देखना Dictionaryहै। गणना करने वाले को कॉपी किए गए डेटा के साथ कैश किया जाएगा, और एक नया एन्यूमरेशन शुरू करने से पहले पूछताछ की जाएगी। एक अपवाद के मामले में कैश्ड कॉपी को छोड़ दिया जाएगा, और एक नया बनाया जाएगा। यहाँ इस विचार का मेरा कार्यान्वयन है:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

public class EnumerableSnapshot<T> : IEnumerable<T>, IDisposable
{
    private IEnumerable<T> _source;
    private IEnumerator<T> _enumerator;
    private ReadOnlyCollection<T> _cached;

    public EnumerableSnapshot(IEnumerable<T> source)
    {
        _source = source ?? throw new ArgumentNullException(nameof(source));
    }

    public IEnumerator<T> GetEnumerator()
    {
        if (_source == null) throw new ObjectDisposedException(this.GetType().Name);
        if (_enumerator == null)
        {
            _enumerator = _source.GetEnumerator();
            _cached = new ReadOnlyCollection<T>(_source.ToArray());
        }
        else
        {
            var modified = false;
            if (_source is ICollection collection) // C# 7 syntax
            {
                modified = _cached.Count != collection.Count;
            }
            if (!modified)
            {
                try
                {
                    _enumerator.MoveNext();
                }
                catch (InvalidOperationException)
                {
                    modified = true;
                }
            }
            if (modified)
            {
                _enumerator.Dispose();
                _enumerator = _source.GetEnumerator();
                _cached = new ReadOnlyCollection<T>(_source.ToArray());
            }
        }
        return _cached.GetEnumerator();
    }

    public void Dispose()
    {
        _enumerator?.Dispose();
        _enumerator = null;
        _cached = null;
        _source = null;
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

public static class EnumerableSnapshotExtensions
{
    public static EnumerableSnapshot<T> ToEnumerableSnapshot<T>(
        this IEnumerable<T> source) => new EnumerableSnapshot<T>(source);
}

उपयोग उदाहरण:

private static IDictionary<Guid, Subscriber> _subscribers;
private static EnumerableSnapshot<Subscriber> _subscribersSnapshot;

//...(in the constructor)
_subscribers = new Dictionary<Guid, Subscriber>();
_subscribersSnapshot = _subscribers.Values.ToEnumerableSnapshot();

// ...(elsewere)
foreach (var subscriber in _subscribersSnapshot)
{
    //...
}

दुर्भाग्यवश इस विचार का उपयोग वर्तमान Dictionaryमें .NET कोर 3.0 में वर्ग के साथ नहीं किया जा सकता है , क्योंकि यह वर्ग संग्रह को फेंक नहीं देता है जब गणना और विधियों को संशोधित किया जाता है Removeऔर Clearइसे लागू किया जाता है। मेरे द्वारा चेक किए गए अन्य सभी कंटेनर लगातार व्यवहार कर रहे हैं। मैं योजनाबद्ध तरीके से इन कक्षाओं में जांच की गई: List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V>और SortedDictionary<T,V>Dictionary.NET कोर में केवल कक्षा के दो उपर्युक्त तरीके गणना को अमान्य नहीं कर रहे हैं।


अद्यतन: मैंने कैश्ड की लंबाई और मूल संग्रह की तुलना करके उपरोक्त समस्या को ठीक किया। यह फिक्स मानता है कि डिक्शनरी को सीधे एक तर्क के रूप में पारित किया जाएगा EnumerableSnapshot, और इसकी पहचान को (उदाहरण के लिए) एक प्रक्षेपण द्वारा छिपाया नहीं जाएगा जैसे dictionary.Select(e => e).ΤοEnumerableSnapshot():।


महत्वपूर्ण: उपरोक्त वर्ग सुरक्षित नहीं है । यह एक ही धागे में विशेष रूप से चल रहे कोड से उपयोग करने का इरादा है।


0

आप सब्सक्राइबर डिक्शनरी ऑब्जेक्ट को एक ही प्रकार की अस्थायी डिक्शनरी ऑब्जेक्ट में कॉपी कर सकते हैं और फिर फ़ॉरच लूप का उपयोग करके अस्थायी शब्दकोश ऑब्जेक्ट को पुन: टाइप कर सकते हैं।


(यह पोस्ट प्रश्न का एक गुणवत्ता उत्तर प्रदान करने के लिए प्रतीत नहीं होता है । कृपया या तो अपने उत्तर को संपादित करें और, या इसे प्रश्न के लिए एक टिप्पणी के रूप में पोस्ट करें)।
s --unıɐ ɐ qɐp

0

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


0

वहाँ एक लिंक है जहाँ यह बहुत अच्छी तरह से विस्तृत है और समाधान भी दिया गया है। अगर आपको उचित समाधान मिल गया है तो कृपया यहाँ पोस्ट करें ताकि अन्य समझ सकें। दिए गए समाधान ठीक है तो पोस्ट की तरह अन्य भी इन समाधान की कोशिश कर सकते हैं।

आपके लिए मूल लिंक का संदर्भ: - https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/

जब हम एक वस्तु को क्रमबद्ध करने के लिए .Net सीरियलाइज़ेशन कक्षाओं का उपयोग करते हैं, जहां इसकी परिभाषा में एक Enumerable प्रकार, अर्थात संग्रह होता है, तो आपको आसानी से InvalidOperationException कहा जा रहा होगा "संग्रह संशोधित किया गया था; गणन ऑपरेशन निष्पादित नहीं हो सकता" जहां कोडिंग मल्टी-थ्रेड परिदृश्यों के अंतर्गत हो। नीचे का कारण यह है कि सीरिमलाइज़ेशन कक्षाएं संग्रहकर्ता के माध्यम से संग्रह के माध्यम से पुनरावृत्ति करेंगी, जैसे, समस्या इसे संशोधित करते हुए संग्रह के माध्यम से पुनरावृति करने की कोशिश करती है।

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

खैर, .Net 4.0 जो बहु-थ्रेडिंग परिदृश्यों के साथ काम करता है। इस क्रमबद्ध संग्रह क्षेत्र की समस्या के लिए, मैंने पाया कि हम सिर्फ समवर्ती क्यू (चेक MSDN) वर्ग से लाभ उठा सकते हैं, जो एक थ्रेड-सुरक्षित और फीफो संग्रह है और कोड लॉक-फ़्री बनाता है।

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

BTW, कॉन्ट्रैक्चुअल क्यू नॉट इन के पास एल्गोरिथम होने के कारण क्लियर तरीका है जो कलेक्शन के एटोमिकली क्लीयर होने की अनुमति नहीं देता है। इसलिए आपको इसे स्वयं करना होगा, सबसे तेज़ तरीका एक प्रतिस्थापन के लिए एक नया खाली समवर्ती क्यूब्यू्यू बनाना है।


-1

मैं व्यक्तिगत रूप से हाल ही में इस अपरिचित कोड में भाग गया था, जहां यह Linq's के साथ एक खुले dbcontext में था। -Remove (आइटम)। मेरे पास त्रुटि डिबगिंग का पता लगाने का एक समय था क्योंकि आइटम (ओं) को पहली बार पुनरावृत्ति हटा दिया गया था, और अगर मैंने पीछे हटने और फिर से कदम बढ़ाने की कोशिश की, तो संग्रह खाली था, क्योंकि मैं उसी संदर्भ में था!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.