সংগ্রহটি পরিবর্তন করা হয়েছিল; গণনা ক্রিয়াকলাপ চালানো হতে পারে না


908

আমি এই ত্রুটির তলটিতে পৌঁছতে পারি না, কারণ যখন ডিবাগারটি সংযুক্ত করা হয় তখন মনে হয় না। নীচে কোড দেওয়া আছে।

এটি একটি উইন্ডোজ পরিষেবাতে একটি ডাব্লুসিএফ সার্ভার। যখনই কোনও ডেটা ইভেন্ট হয় (যখন এলোমেলো বিরতিতে হয়, তবে প্রায়শই প্রায় নয় - প্রতিদিন প্রায় 800 বার) পরিষেবাটি বিজ্ঞপ্তিপ্রাপ্তদের পরিষেবা বলে।

যখন কোনও উইন্ডোজ ফর্ম ক্লায়েন্ট সাবস্ক্রাইব করে, সাবস্ক্রাইবার আইডি সাবস্ক্রাইবার অভিধানে যুক্ত হয় এবং যখন ক্লায়েন্ট সাবস্ক্রাইব করে তখন অভিধান থেকে এটি মুছে ফেলা হয়। ত্রুটি ঘটে যখন (বা পরে) কোনও ক্লায়েন্ট সাবস্ক্রাইব করে। এটি প্রদর্শিত হবে যে পরের বার 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);
        }
    }
}

আমার ক্ষেত্রে এটি একটি সমান্তরাল প্রভাব ছিল কারণ আমি কয়েকটি ব্যবহার করছি Iআরঙ্কিত ("টেবিল") যা প্রক্রিয়া চলাকালীন পরিবর্তিত হয়েছিল - কোডটি পড়ার সময় খুব স্পষ্ট নয়। তবে আমি ভাগ্যবান যে Inc সমস্ত Includes প্রয়োজন ছিল না (হ্যাঁ, পুরাতন, অবিচ্ছিন্ন কোড) এবং আমি কেবল তাদের মুছে ফেলার মাধ্যমে আমার সমস্যাটি সমাধান করেছি
আদি

উত্তর:


1630

সম্ভবত যা ঘটছে তা হ'ল সিগন্যালডাটা অপ্রত্যক্ষভাবে লুপ চলাকালীন গ্রাহকদের অভিধানটি হুডের অধীনে পরিবর্তন করে সেই বার্তাটিতে নিয়ে আসে। আপনি এটি পরিবর্তন করে যাচাই করতে পারেন

foreach(Subscriber s in subscribers.Values)

প্রতি

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

আমি যদি ঠিক আছি তবে সমস্যাটি বিলুপ্ত হবে

শুরুতে পৃথক তালিকার subscribers.Values.ToList()মানগুলির অনুলিপিগুলি কল করা । এই তালিকায় অন্য কোনও কিছুর অ্যাক্সেস নেই (এটির একটি ভেরিয়েবল নামও নেই!), তাই কিছুই এটিকে লুপের ভিতরে পরিবর্তন করতে পারে না।subscribers.Valuesforeach


14
BTW .ToList () System.Core dll এ উপস্থিত রয়েছে যা .NET 2.0 অ্যাপ্লিকেশনগুলির সাথে সামঞ্জস্যপূর্ণ নয়। সুতরাং আপনার আপনার টার্গেট অ্যাপটি নেট। 3.5
মিসাল 153

60
আমি বুঝতে পারছি না কেন আপনি একটি ToList করেনি এবং কেন যে সংশোধন করা হয়েছে সবকিছু
PositiveGuy

204
@ কফিএডিক্টিক্ট: বিষয়টি হ'ল লুপের subscribers.Valuesঅভ্যন্তরে পরিবর্তন করা হচ্ছে foreach। শুরুতে পৃথক তালিকার subscribers.Values.ToList()মানগুলির অনুলিপিগুলি কল করা । এই তালিকায় অন্য কোনও কিছুর অ্যাক্সেস নেই (এটির একটি ভেরিয়েবল নামও নেই!) , তাই কিছুই এটিকে লুপের ভিতরে পরিবর্তন করতে পারে না। subscribers.Valuesforeach
ব্লুরাজা - ড্যানি পিফ্লুঘুফ্ট

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
আমি যদি আশা করি যে আপনি যদি বড় সংগ্রহের সাথে কাজ করছেন তবে তা বিবেচনাযোগ্য হবে। এটি যদি ছোট হয় তবে আমি সম্ভবত টোললিস্ট করে এগিয়ে যেতে চাই।
কার্ল কেইঞ্জার 21'15

42

আপনি যখনই লুপ করা হচ্ছে তখন এটি পরিবর্তন করতে বাধা দেওয়ার জন্য আপনার গ্রাহকদের অভিধানও লক করতে পারেন:

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

2
এটি কি সম্পূর্ণ উদাহরণ? আমার কাছে একটি শ্রেণি রয়েছে (নীচে ডিকোরিজ অবজেক্ট) যার একটি জেনেরিক অভিধান রয়েছে <স্ট্রিং, ইনট্রেড> নামের মার্কারফ্রিকোয়েন্সি, তবে এটি করার সাথে সাথে ক্রাশটি তাত্ক্ষণিকভাবে সমাধান করতে পারেনি: লক (_ডায়ারশন.মার্কারফ্রান্সিয়েন্স) {ফোর্যাচ (কীভ্যালিউপায়ার <স্ট্রিং, ইনট> জুটি) _ডায়ারশন.মার্কার ফ্রিকোয়েন্সি) {...}}
জন

4
@ জ্যাকউমস এটি সম্ভব যে আপনি সম্ভবত সংশোধন করছেন, সম্ভবত লকটির ভিতরেই অভিধানটি পুনরায় বরাদ্দ করছেন MarkerFrequencies, এর অর্থ হ'ল মূল উদাহরণটি আর লক করা নেই। এছাড়াও ব্যবহার করে একটি চেষ্টা forএকটি পরিবর্তে foreach, দেখ এই এবং এই । যদি এটি সমাধান করে তবে আমাকে জানান।
মোহাম্মদ সিপাহাভন্ড

1
ঠিক আছে, অবশেষে আমি এই বাগটি ঠিক করতে পেরেছি এবং এই টিপস সহায়ক ছিল - ধন্যবাদ! আমার ডেটাসেটটি ছোট ছিল এবং এই অপারেশনটি কেবলমাত্র একটি ইউআই ডিসপ্লে আপডেট ছিল, সুতরাং অনুলিপিটি অনুলিপি করা ভাল মনে হয়েছিল। (আমি উভয় থ্রেডে মার্কার ফ্রিকোয়েন্সিগুলি লক করার পরে ফোরচের পরিবর্তে ব্যবহারের জন্যও পরীক্ষা-নিরীক্ষা করেছি This এটি ক্র্যাশটিকে আটকেছিল তবে আরও ডিবাগিংয়ের কাজ প্রয়োজন বলে মনে হয়েছে And এবং এটি আরও জটিলতার পরিচয় দিয়েছে two এখানে দুটি থ্রেড থাকার আমার একমাত্র কারণটি ব্যবহারকারীকে বাতিল করতে সক্ষম করা একটি অপারেশন, যাইহোক, ইউআই সরাসরি সেই তথ্যটি সংশোধন করে না))
জন কোম্বস

9
সমস্যাটি হ'ল বড় অ্যাপ্লিকেশনগুলির জন্য, লকগুলি একটি বড় কার্যকারিতা হিট হতে পারে - System.Collections.Concurrentনেমস্পেসে কোনও সংগ্রহ ব্যবহার করা ভাল ।
BrainSlugs83

28

কেন এই ত্রুটি?

সাধারণভাবে। নেট সংগ্রহগুলি একই সাথে গণনা করা ও সংশোধন করা সমর্থন করে না। আপনি যদি গণনার সময় সংগ্রহের তালিকাটি পরিবর্তন করার চেষ্টা করেন তবে এটি একটি ব্যতিক্রম উত্থাপন করে। সুতরাং এই ত্রুটির পিছনে সমস্যাটি হ'ল আমরা যখন লুপিংয়ের সময় তালিকাগুলি / অভিধানটি সংশোধন করতে পারি না।

একটি সমাধান

আমরা যদি কোনও অভিধানটির কীগুলির তালিকা ব্যবহার করে এর পুনরুক্তি করি, সমান্তরালভাবে আমরা অভিধান অবজেক্টটি সংশোধন করতে পারি, কারণ আমরা কী-সংগ্রহের মাধ্যমে পুনরাবৃত্তি করছি এবং অভিধানটি নয় (এবং এর মূল সংগ্রহটি পুনরাবৃত্তি করছি)।

উদাহরণ

//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;
}

এই সমাধান সম্পর্কে এখানে একটি ব্লগ পোস্ট

এবং স্ট্যাকওভারফ্লোতে গভীর ডুব দেওয়ার জন্য: কেন এই ত্রুটি ঘটে?


ধন্যবাদ! এটি কেন হয় তার একটি স্পষ্ট ব্যাখ্যা এবং তাত্ক্ষণিকভাবে আমাকে আমার আবেদনে এটি সমাধান করার উপায় দিয়েছিল।
কিম ক্রোসার

5

আসলে সমস্যাটি আমার কাছে মনে হচ্ছে আপনি তালিকা থেকে উপাদানগুলি সরিয়ে করছেন এবং তালিকাটি পড়া চালিয়ে যাওয়ার প্রত্যাশা করছেন যেন কিছুই ঘটেছিল না।

আপনার সত্যিকারের যা করা দরকার তা হ'ল শেষ থেকে শুরু করে শুরু করা শুরু করা। এমনকি আপনি তালিকা থেকে উপাদানগুলি সরিয়ে ফেললেও আপনি এটি পড়া চালিয়ে যেতে সক্ষম হবেন।


আমি দেখতে পাচ্ছি না যে কিভাবে কোন পার্থক্য হবে? আপনি যদি তালিকার মাঝখানে কোনও উপাদান সরিয়ে ফেলেন তবে এটি যে আইটেমটি অ্যাক্সেস করার চেষ্টা করছে তা পাওয়া যাওয়ায় এটি এখনও একটি ত্রুটি ছুঁড়ে ফেলবে?
Zapnologica

4
@ জাপানোলজিকার পার্থক্যটি হ'ল - আপনি তালিকাটি গণনা করবেন না - প্রতিটি / একবারের জন্য করার পরিবর্তে আপনি পরবর্তী / এর জন্য একটি করছেন এবং পূর্ণসংখ্যার মাধ্যমে এটি অ্যাক্সেস করতে চান - আপনি অবশ্যই একটি তালিকাতে একটি তালিকা পরিবর্তন করতে পারেন জন্য / পরবর্তী লুপ করি কিন্তু সেই জন্য / প্রতিটি লুপ (কারণ জন্য / প্রতিটি উল্লেখ ) - আপনার কাছে এটি একটি জন্য / পরবর্তী মধ্যে ফরোয়ার্ড যাচ্ছে কি করতে পারেন, আপনি আপনার কাউন্টার, ইত্যাদি সমন্বয় অতিরিক্ত যুক্তি আছে উপলব্ধ
BrainSlugs83

4

অবৈধ অপেরেশন এক্সসেপশন- একটি অবৈধ অপেরেশন এক্সসেপশন ঘটেছে। এটি পূর্বাভাস-লুপে "সংগ্রহটি সংশোধিত হয়েছিল" বলে প্রতিবেদন করে

ব্রেক স্টেটমেন্ট ব্যবহার করুন, একবার বস্তুটি সরানো হবে।

উদা:

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 (এলিমেন্ট "এ") অবস্থানে উপাদানটি সরিয়ে ফেলুন। এর পরে সমস্ত অ্যারে উপাদানগুলি একটি অবস্থানকে উপরে নিয়ে যাবে এবং অ্যারেটি ["b", "c"] হবে। সুতরাং, পরবর্তী পুনরাবৃত্তিতে (i = 1) আপনি অবস্থান 1 এ উপাদানটি পরীক্ষা করবেন যা "সি" নয় "বি" হবে। এটা ভুল. এটি ঠিক করতে, আপনাকে নীচ থেকে শীর্ষে যেতে হবে
কাস্পারস ওজলস

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 collection = new ListItemCollection();
            foreach (ListItem item in ListBox1.Items)
            {
                if (item.Selected && !collection.Contains(item))
                    collection.Add(item);
            }

এই কোডটি কীভাবে নকলকে ডাটাবেসে প্রবেশ করা থেকে আটকাবে prevent আমি অনুরূপ কিছু লিখেছি এবং যখন আমি তালিকা বাক্স থেকে নতুন ব্যবহারকারী যুক্ত করি, আমি যদি ইতিমধ্যে তালিকায় থাকা এমন একটি নির্বাচিত রাখি তবে এটি একটি সদৃশ এন্ট্রি তৈরি করবে। আপনার কাছে কি কোনও পরামর্শ নেই মাইক?
জেমি

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, .নেট কোর 3.0 কারণ এই শ্রেণীর নিক্ষেপ করে না সংগ্রহ পরিবর্তন হয়েছিল ব্যতিক্রম যখন গণিত ও পদ্ধতি Removeএবং Clearপ্রার্থনা করা হয়। অন্য যে সমস্ত ধারক আমি পরীক্ষা করেছি তারা নিয়মিত আচরণ করছে। আমি ধারাক্রমে এই শ্রেণীর পরীক্ষিত: List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V>এবং SortedDictionary<T,V>DictionaryNET কোরে শ্রেণীর দুটি পূর্বোক্ত পদ্ধতিই গণনাটিকে অকার্যকর করছে না।


আপডেট: ক্যাশেড এবং মূল সংগ্রহের দৈর্ঘ্যের সাথে তুলনা করে আমি উপরের সমস্যাটি ঠিক করেছি। এই ফিক্স ধরে নেয় যে অভিধান একটি আর্গুমেন্ট হিসাবে সরাসরি গৃহীত হবে EnumerableSnapshotএর কন্সট্রাকটর, এবং তার পরিচয় (উদাহরণস্বরূপ) দ্বারা লুকানো করা হইনি মত একটি অভিক্ষেপ: dictionary.Select(e => e).ΤοEnumerableSnapshot()


গুরুত্বপূর্ণ: উপরে ক্লাস হয় না নিরাপদ থ্রেড। এটি একক থ্রেডে একচেটিয়াভাবে চলমান কোড থেকে ব্যবহারের উদ্দেশ্যে।


0

আপনি গ্রাহকরা অভিধান অবজেক্টটি একই ধরণের অস্থায়ী অভিধান অবজেক্টে অনুলিপি করতে পারেন এবং তারপরে সামনের অভিধান লুপটি ফোরচ লুপ ব্যবহার করে পুনরাবৃত্তি করতে পারেন।


(এই পোস্টটি প্রশ্নের কোনও মানের উত্তর সরবরাহ করে বলে মনে হচ্ছে না Please দয়া করে হয় আপনার উত্তরটি সম্পাদনা করুন এবং, অথবা এটি কেবল প্রশ্নের মন্তব্য হিসাবে পোস্ট করুন)।
সুনুন ןɐ কিউপি

0

সুতরাং এই সমস্যাটি সমাধানের একটি ভিন্ন উপায় হ'ল উপাদানগুলি মুছে ফেলার পরিবর্তে একটি নতুন অভিধান তৈরি করা হবে এবং কেবল যে উপাদানগুলিকে সরাতে চান না তা যুক্ত করুন তারপরে মূল অভিধানটি নতুনটির সাথে প্রতিস্থাপন করুন। আমি মনে করি না যে এটি কোনও দক্ষতার সমস্যা খুব বেশি কারণ এটি কাঠামোর উপরে আপনি পুনরাবৃত্তি করার সংখ্যা বাড়ায় না।


0

একটি লিঙ্ক রয়েছে যেখানে এটি খুব ভালভাবে ব্যাখ্যা করা হয়েছে এবং সমাধানও দেওয়া হয়েছে। আপনি যদি সঠিক সমাধান পেয়ে থাকেন তবে চেষ্টা করুন দয়া করে এখানে পোস্ট করুন যাতে অন্যেরা বুঝতে পারে। প্রদত্ত সমাধানটি ঠিক আছে তবে পোস্টের মতো তাই অন্যরাও এই সমাধানগুলি চেষ্টা করতে পারেন।

আপনার জন্য মূল লিঙ্কটি উল্লেখ করুন: - https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/

যখন আমরা কোনও আইটেমকে সিরিয়ালাইজ করার জন্য নেট সিরিয়ালাইজ ক্লাস ব্যবহার করি যেখানে এর সংজ্ঞাটিতে একটি পরিগণিত প্রকার, অর্থাত্ সংগ্রহ রয়েছে, আপনি সহজেই অবৈধ অপারেশনটি পেয়ে যাবেন "সংগ্রহটি সংশোধিত হয়েছিল; গণনা অপারেশন চালিত হতে পারে না" যেখানে আপনার কোডিং মাল্টি-থ্রেড পরিস্থিতিতে রয়েছে। মূল কারণ হ'ল সিরিয়ালাইজেশন ক্লাসগুলি গণকের মাধ্যমে সংগ্রহের মাধ্যমে পুনরাবৃত্তি হবে, যেমন, সমস্যাটি সংশোধন করার সময় কোনও সংগ্রহের মাধ্যমে পুনরাবৃত্তি করার চেষ্টা করতে যায়।

প্রথম সমাধান, আমরা তালিকার অবজেক্টটিতে একবারে কেবল একটি থ্রেড থেকে চালানো যেতে পারে তা নিশ্চিত করার জন্য লকটি কেবল একটি সিঙ্ক্রোনাইজেশন সমাধান হিসাবে ব্যবহার করতে পারি। স্পষ্টতই, আপনি পারফরম্যান্স পেনাল্টি পাবেন যে আপনি যদি সেই অবজেক্টের কোনও সংগ্রহ সিরিয়াল করতে চান, তবে তাদের প্রত্যেকের জন্য, লকটি প্রয়োগ করা হবে।

ওয়েল,। নেট 4.0 যা মাল্টি-থ্রেডিং পরিস্থিতিগুলির সাথে কাজ করে। সংগ্রহের ক্ষেত্রের এই ক্রিয়াকলাপের সমস্যার জন্য, আমি জানতে পেরেছিলাম যে আমরা কেবল কনক্রেনিউকিউ (এমএসডিএন পরীক্ষা করুন) ক্লাস থেকে সুবিধা নিতে পারি, এটি একটি থ্রেড-নিরাপদ এবং ফিফো সংগ্রহ এবং কোড লক-মুক্ত করে।

এই শ্রেণিটি ব্যবহার করে, এর সরলতায়, আপনার কোডের জন্য আপনার যে জিনিসটি সংশোধন করতে হবে তা সংগ্রহের প্রকারটি এর সাথে প্রতিস্থাপন করছে, সমকালীন কিউয়ের শেষে একটি উপাদান যুক্ত করতে এনেকিউ ব্যবহার করুন, সেই লক কোডটি সরিয়ে দিন। অথবা, আপনি যে দৃশ্যে কাজ করছেন তার জন্য যদি তালিকার মতো সংগ্রহের জিনিসগুলির প্রয়োজন হয় তবে আপনার ক্ষেত্রগুলিতে সাম্প্রতিক কুইউ মানিয়ে নিতে আরও কয়েকটি কোডের প্রয়োজন হবে।

অন্তর্নিহিত অ্যালগরিদমের কারণে বিটিডাব্লু, কনকন্টারকিউয়ের একটি পরিষ্কার পদ্ধতি নেই যা সংগ্রহকে পরমাণুভাবে সাফ করার অনুমতি দেয় না। সুতরাং আপনাকে এটি নিজেই করতে হবে, দ্রুততম উপায়টি হ'ল একটি প্রতিস্থাপনের জন্য একটি নতুন খালি কনক্রন্টকিউ পুনরায় তৈরি করা।


-1

আমি ব্যক্তিগতভাবে এটি সম্প্রতি অপরিচিত কোডে ছড়িয়ে দিয়েছি যেখানে এটি লিনকের সাথে একটি উন্মুক্ত dbcontext এ ছিল R সরান (আইটেম)। আমার ত্রুটি ডিবাগিংয়ের সময়সীমা নির্ধারণ করা ছিল কারণ আইটেম (গুলি) প্রথমবার পুনরাবৃত্তি করে মুছে ফেলা হয়েছে, এবং যদি আমি পিছনে গিয়ে আবার পদক্ষেপ নেওয়ার চেষ্টা করি তবে সংগ্রহটি খালি ছিল, যেহেতু আমি একই প্রসঙ্গে ছিলাম!

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.