Iterator function for IDisposable items - c#

Suppose I want to create an iterator function that yields IDisposable items.
IEnumerable<Disposable> GetItems()
{
yield return new Disposable();
yield return new Disposable();
}
This does not seem ideal for the client code:
foreach (var item in source.GetItems())
{
using (item)
{
// ...
}
}
Intuitively, the using comes too late. Things could get moved around. One could accidentally insert code between the foreach and the using. Not ideal.
I am looking for a better alternative!
One approach that comes to mind is creating the following API instead of an iterator function:
// Client
while (source.HasItem)
{
using (var item = source.GetNextItem())
{
// ...
}
}
// Source
private IEnumerator<Disposable> Enumerator { get; }
private bool? _hasItem;
bool HasItem
{
get
{
if (this._hasItem == null) this._hasItem = this.Enumerator.MoveNext();
return this._hasItem;
}
}
Disposable GetNextItem()
{
if (!this.HasItem) throw new IndexOutOfBoundsException();
this._hasItem = null;
return this.Enumerator.Current;
}
But now it seems that the source has to become IDisposable! How else would it know when to dispose Enumerator? That can be an unpleasant side-effect.
I am looking for an alternative that feels solid in the client, but that keeps the source from becoming IDisposable too.
Edit - Clarification: I forgot to mention that some of the content that we need comes from an iterator. Concretely, imagine that we are returning IDbCommand instances, which are IDisposable. Before returning each command, we need to populate it with some query data, which in turn comes from a simple iterator method.

If I understand correctly, the following pattern works for you
foreach (var item in source.GetItems())
{
using (item)
{
// ...
}
}
but the potential problem is putting some code outside the using block. So why don't you just wrap that logic in a custom extensions method:
public static class EnumerableExtennisons
{
public static IEnumerable<T> WithUsing<T>(this IEnumerable<T> source)
where T : IDisposable
{
foreach (var item in source)
{
using (item)
yield return item;
}
}
}
This way you ensure the item is wrapped in using block *before** returning it to the caller, so there is no way the caller to insert code before/after it. The C# compiler generated code ensures the item.Dispose is called in either MoveNext or Dispose method of the IEnumerator<T> (in case the iteration ends earlier).
The usage would be to append .WithUsing() call instead of using block where needed:
foreach (var item in source.GetItems().WithUsing())
{
// ...
}

We can expose only an IEnumerator<T>, which supports nothing but MoveNext() and Current.
Now, the underlying, private iterator function can be streaming, taking care of disposing the items. No invalid operations are introduced to the client - unless they try to store the borrowed objects and try to use them later, where it becomes clear that the objects are already disposed.
// Client
using (var itemEnumerator = source.GetItemEnumerator())
{
while (itemEnumerator.MoveNext())
var current = itemEnumerator.Current;
}
// Source
IEnumerator<Disposable> GetItemEnumerator() => this.StreamItems().GetEnumerator();
private IEnumerable<Disposable> StreamItems()
{
while (this.ShouldCreateItem())
{
using (var item = this.CreateItem())
{
yield return item;
}
}
}

The iterator function itself can take responsibility for placing a using (i.e. disposing the current item when the next is requested).
IEnumerable<Disposable> GetItems()
{
// Assumming CreateItems() is an iterator as well
foreach (var item in this.CreateItems())
using (item) yield return item;
}
As a result, we will essentially have a streaming-only iterator. Methods like ToList() become meaningless, because only one item is usable at a time.
Unfortunately, this does create room for error.

Related

C# Exception while iterating through collection of Values of Dictionary of (int Key,<T> Value) [duplicate]

I can't get to the bottom of this error, because when the debugger is attached, it does not seem to occur.
Collection was modified; enumeration operation may not execute
Below is the code.
This is a WCF server in a Windows service. The method NotifySubscribers() is called by the service whenever there is a data event (at random intervals, but not very often - about 800 times per day).
When a Windows Forms client subscribes, the subscriber ID is added to the subscribers dictionary, and when the client unsubscribes, it is deleted from the dictionary. The error happens when (or after) a client unsubscribes. It appears that the next time the NotifySubscribers() method is called, the foreach() loop fails with the error in the subject line. The method writes the error into the application log as shown in the code below. When a debugger is attached and a client unsubscribes, the code executes fine.
Do you see a problem with this code? Do I need to make the dictionary thread-safe?
[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);
}
}
}
What's likely happening is that SignalData is indirectly changing the subscribers dictionary under the hood during the loop and leading to that message. You can verify this by changing
foreach(Subscriber s in subscribers.Values)
To
foreach(Subscriber s in subscribers.Values.ToList())
If I'm right, the problem will disappear.
Calling subscribers.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach. Nothing else has access to this list (it doesn't even have a variable name!), so nothing can modify it inside the loop.
When a subscriber unsubscribes you are changing contents of the collection of Subscribers during enumeration.
There are several ways to fix this, one being changing the for loop to use an explicit .ToList():
public void NotifySubscribers(DataRecord sr)
{
foreach(Subscriber s in subscribers.Values.ToList())
{
^^^^^^^^^
...
A more efficient way, in my opinion, is to have another list that you declare that you put anything that is "to be removed" into. Then after you finish your main loop (without the .ToList()), you do another loop over the "to be removed" list, removing each entry as it happens. So in your class you add:
private List<Guid> toBeRemoved = new List<Guid>();
Then you change it to:
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 );
}
This will not only solve your problem, it will prevent you from having to keep creating a list from your dictionary, which is expensive if there are a lot of subscribers in there. Assuming the list of subscribers to be removed on any given iteration is lower than the total number in the list, this should be faster. But of course feel free to profile it to be sure that's the case if there's any doubt in your specific usage situation.
Why this error?
In general .Net collections do not support being enumerated and modified at the same time. If you try to modify the collection list during enumeration, it raises an exception. So the issue behind this error is, we can not modify the list/dictionary while we are looping through the same.
One of the solutions
If we iterate a dictionary using a list of its keys, in parallel we can modify the dictionary object, as we are iterating through the key-collection and
not the dictionary(and iterating its key collection).
Example
//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;
}
Here is a blog post about this solution.
And for a deep dive in StackOverflow: Why this error occurs?
Okay so what helped me was iterating backwards. I was trying to remove an entry from a list but iterating upwards and it screwed up the loop because the entry didn't exist anymore:
for (int x = myList.Count - 1; x > -1; x--)
{
myList.RemoveAt(x);
}
The accepted answer is imprecise and incorrect in the worst case . If changes are made during ToList(), you can still end up with an error. Besides lock, which performance and thread-safety needs to be taken into consideration if you have a public member, a proper solution can be using immutable types.
In general, an immutable type means that you can't change the state of it once created.
So your code should look like:
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
}
}
//.........
}
This can be especially useful if you have a public member. Other classes can always foreach on the immutable types without worrying about the collection being modified.
I want to point out other case not reflected in any of the answers. I have a Dictionary<Tkey,TValue> shared in a multi threaded app, which uses a ReaderWriterLockSlim to protect the read and write operations. This is a reading method that throws the exception:
public IEnumerable<Data> GetInfo()
{
List<Data> info = null;
_cacheLock.EnterReadLock();
try
{
info = _cache.Values.SelectMany(ce => ce.Data); // Ad .Tolist() to avoid exc.
}
finally
{
_cacheLock.ExitReadLock();
}
return info;
}
In general, it works fine, but from time to time I get the exception. The problem is a subtlety of LINQ: this code returns an IEnumerable<Info>, which is still not enumerated after leaving the section protected by the lock. So, it can be changed by other threads before being enumerated, leading to the exception. The solution is to force the enumeration, for example with .ToList() as shown in the comment. In this way, the enumerable is already enumerated before leaving the protected section.
So, if using LINQ in a multi-threaded application, be aware to always materialize the queries before leaving the protected regions.
InvalidOperationException-
An InvalidOperationException has occurred. It reports a "collection was modified" in a foreach-loop
Use break statement, Once the object is removed.
ex:
ArrayList list = new ArrayList();
foreach (var item in list)
{
if(condition)
{
list.remove(item);
break;
}
}
Actually the problem seems to me that you are removing elements from the list and expecting to continue to read the list as if nothing had happened.
What you really need to do is to start from the end and back to the begining. Even if you remove elements from the list you will be able to continue reading it.
I had the same issue, and it was solved when I used a for loop instead of 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);
}
I've seen many options for this but to me this one was the best.
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected)
collection.Add(item);
}
Then simply loop through the collection.
Be aware that a ListItemCollection can contain duplicates. By default there is nothing preventing duplicates being added to the collection. To avoid duplicates you can do this:
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected && !collection.Contains(item))
collection.Add(item);
}
This way should cover a situation of concurrency when the function is called again while is still executing (and items need used only once):
while (list.Count > 0)
{
string Item = list[0];
list.RemoveAt(0);
// do here what you need to do with item
}
If the function get called while is still executing items will not reiterate from the first again as they get deleted as soon as they get used.
Should not affect performance much for small lists.
There is one link where it elaborated very well & solution is also given.
Try it if you got proper solution please post here so other can understand.
Given solution is ok then like the post so other can try these solution.
for you reference original link :-
https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/
When we use .Net Serialization classes to serialize an object where its definition contains an Enumerable type, i.e.
collection, you will be easily getting InvalidOperationException saying "Collection was modified;
enumeration operation may not execute" where your coding is under multi-thread scenarios.
The bottom cause is that serialization classes will iterate through collection via enumerator, as such,
problem goes to trying to iterate through a collection while modifying it.
First solution, we can simply use lock as a synchronization solution to ensure that
the operation to the List object can only be executed from one thread at a time.
Obviously, you will get performance penalty that
if you want to serialize a collection of that object, then for each of them, the lock will be applied.
Well, .Net 4.0 which makes dealing with multi-threading scenarios handy.
for this serializing Collection field problem, I found we can just take benefit from ConcurrentQueue(Check MSDN)class,
which is a thread-safe and FIFO collection and makes code lock-free.
Using this class, in its simplicity, the stuff you need to modify for your code are replacing Collection type with it,
use Enqueue to add an element to the end of ConcurrentQueue, remove those lock code.
Or, if the scenario you are working on do require collection stuff like List, you will need a few more code to adapt ConcurrentQueue into your fields.
BTW, ConcurrentQueue doesnât have a Clear method due to underlying algorithm which doesnât permit atomically clearing of the collection.
so you have to do it yourself, the fastest way is to re-create a new empty ConcurrentQueue for a replacement.
Here is a specific scenario that warrants a specialized approach:
The Dictionary is enumerated frequently.
The Dictionary is modified infrequently.
In this scenario creating a copy of the Dictionary (or the Dictionary.Values) before every enumeration can be quite costly. My idea about solving this problem is to reuse the same cached copy in multiple enumerations, and watch an IEnumerator of the original Dictionary for exceptions. The enumerator will be cached along with the copied data, and interrogated before starting a new enumeration. In case of an exception the cached copy will be discarded, and a new one will be created. Here is my implementation of this idea:
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);
}
Usage example:
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)
{
//...
}
Unfortunately this idea cannot be used currently with the class Dictionary in .NET Core 3.0, because this class does not throw a Collection was modified exception when enumerated and the methods Remove and Clear are invoked. All other containers I checked are behaving consistently. I checked systematically these classes:
List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V> and SortedDictionary<T,V>. Only the two aforementioned methods of the Dictionary class in .NET Core are not invalidating the enumeration.
Update: I fixed the above problem by comparing also the lengths of the cached and the original collection. This fix assumes that the dictionary will be passed directly as an argument to the EnumerableSnapshot's constructor, and its identity will not be hidden by (for example) a projection like: dictionary.Select(e => e).ΤοEnumerableSnapshot().
Important: The above class is not thread safe. It is intended to be used from code running exclusively in a single thread.
You can copy subscribers dictionary object to a same type of temporary dictionary object and then iterate the temporary dictionary object using foreach loop.
So a different way to solve this problem would be instead of removing the elements create a new dictionary and only add the elements you didnt want to remove then replace the original dictionary with the new one. I don't think this is too much of an efficiency problem because it does not increase the number of times you iterate over the structure.

Why does the using statement work on IEnumerator and what does it do?

Back when I was learning about foreach, I read somewhere that this:
foreach (var element in enumerable)
{
// do something with element
}
is basically equivalent to this:
using (var enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
var element = enumerator.Current;
// do something with element
}
}
Why does this code even compile if neither IEnumerator nor IEnumerator<T> implement IDisposable? C# language specification only seems to mention the using statement in the context of IDisposable.
What does such an using statement do?
Please, check the following link about foreach statement. It uses try/finally block with Dispose call if it's possible. That's the code which is behind using statement.
IEnumerator may not implement IDisposable but GetEnumerator() returns a IEnumerator<T> which does. From the docs on IEnumerator<T>:
In addition, IEnumerator implements IDisposable, which requires you to implement the Dispose method. This enables you to close database connections or release file handles or similar operations when using other resources. If there are no additional resources to dispose of, provide an empty Dispose implementation.
This is of course assuming that your enumeration is an IEnumerable<T> and not just a IEnumerable. If your original enumeration was just an IEnumerable then it wouldn't compile.
It appears no one answered the main question yet:
What does such a using statement do?
I'll go with an example from one of my favorite books - C# in Depth by Jon Skeet. You may have a result of IEnumerator<T> produced by a function like this:
static IEnumerable<string> Iterator()
{
try
{
Console.WriteLine("Before first yield");
yield return "first";
Console.WriteLine("Between yields");
yield return "second";
Console.WriteLine("After second yield");
}
finally
{
Console.WriteLine("In finally block");
}
}
And then you would use it like:
foreach (string value in Iterator())
{
Console.WriteLine("Received value: {0}", value);
if (value != null)
{
break;
}
}
The iterator returns only the first "first" value from the sequence as we break on the very first iteration. Now how your iterator might 'know' that you aren't going to proceed till the end of the loop so it would fire it's finally block? Here comes the hidden using statement. That's how the code from the example above would look like if you couldn’t use a foreach loop:
IEnumerable<string> enumerable = Iterator();
using (IEnumerator<string> enumerator = enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
string value = enumerator.Current;
Console.WriteLine("Received value: {0}", value);
if (value != null)
{
break;
}
}
}
When we leave the scope of the hidden using statement, the Dispose() method fires:
finally
{
Console.WriteLine("In finally block");
}
*All of the code lines and some of the text above are provided by Jon Skeet. I could have rewritten it but decided that I should rather leave it intact. If the author doesn't want me to share it - I'll delete the answer or rewrite it ASAP.

How delete matching from List [duplicate]

I can't get to the bottom of this error, because when the debugger is attached, it does not seem to occur.
Collection was modified; enumeration operation may not execute
Below is the code.
This is a WCF server in a Windows service. The method NotifySubscribers() is called by the service whenever there is a data event (at random intervals, but not very often - about 800 times per day).
When a Windows Forms client subscribes, the subscriber ID is added to the subscribers dictionary, and when the client unsubscribes, it is deleted from the dictionary. The error happens when (or after) a client unsubscribes. It appears that the next time the NotifySubscribers() method is called, the foreach() loop fails with the error in the subject line. The method writes the error into the application log as shown in the code below. When a debugger is attached and a client unsubscribes, the code executes fine.
Do you see a problem with this code? Do I need to make the dictionary thread-safe?
[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);
}
}
}
What's likely happening is that SignalData is indirectly changing the subscribers dictionary under the hood during the loop and leading to that message. You can verify this by changing
foreach(Subscriber s in subscribers.Values)
To
foreach(Subscriber s in subscribers.Values.ToList())
If I'm right, the problem will disappear.
Calling subscribers.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach. Nothing else has access to this list (it doesn't even have a variable name!), so nothing can modify it inside the loop.
When a subscriber unsubscribes you are changing contents of the collection of Subscribers during enumeration.
There are several ways to fix this, one being changing the for loop to use an explicit .ToList():
public void NotifySubscribers(DataRecord sr)
{
foreach(Subscriber s in subscribers.Values.ToList())
{
^^^^^^^^^
...
A more efficient way, in my opinion, is to have another list that you declare that you put anything that is "to be removed" into. Then after you finish your main loop (without the .ToList()), you do another loop over the "to be removed" list, removing each entry as it happens. So in your class you add:
private List<Guid> toBeRemoved = new List<Guid>();
Then you change it to:
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 );
}
This will not only solve your problem, it will prevent you from having to keep creating a list from your dictionary, which is expensive if there are a lot of subscribers in there. Assuming the list of subscribers to be removed on any given iteration is lower than the total number in the list, this should be faster. But of course feel free to profile it to be sure that's the case if there's any doubt in your specific usage situation.
Why this error?
In general .Net collections do not support being enumerated and modified at the same time. If you try to modify the collection list during enumeration, it raises an exception. So the issue behind this error is, we can not modify the list/dictionary while we are looping through the same.
One of the solutions
If we iterate a dictionary using a list of its keys, in parallel we can modify the dictionary object, as we are iterating through the key-collection and
not the dictionary(and iterating its key collection).
Example
//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;
}
Here is a blog post about this solution.
And for a deep dive in StackOverflow: Why this error occurs?
Okay so what helped me was iterating backwards. I was trying to remove an entry from a list but iterating upwards and it screwed up the loop because the entry didn't exist anymore:
for (int x = myList.Count - 1; x > -1; x--)
{
myList.RemoveAt(x);
}
The accepted answer is imprecise and incorrect in the worst case . If changes are made during ToList(), you can still end up with an error. Besides lock, which performance and thread-safety needs to be taken into consideration if you have a public member, a proper solution can be using immutable types.
In general, an immutable type means that you can't change the state of it once created.
So your code should look like:
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
}
}
//.........
}
This can be especially useful if you have a public member. Other classes can always foreach on the immutable types without worrying about the collection being modified.
I want to point out other case not reflected in any of the answers. I have a Dictionary<Tkey,TValue> shared in a multi threaded app, which uses a ReaderWriterLockSlim to protect the read and write operations. This is a reading method that throws the exception:
public IEnumerable<Data> GetInfo()
{
List<Data> info = null;
_cacheLock.EnterReadLock();
try
{
info = _cache.Values.SelectMany(ce => ce.Data); // Ad .Tolist() to avoid exc.
}
finally
{
_cacheLock.ExitReadLock();
}
return info;
}
In general, it works fine, but from time to time I get the exception. The problem is a subtlety of LINQ: this code returns an IEnumerable<Info>, which is still not enumerated after leaving the section protected by the lock. So, it can be changed by other threads before being enumerated, leading to the exception. The solution is to force the enumeration, for example with .ToList() as shown in the comment. In this way, the enumerable is already enumerated before leaving the protected section.
So, if using LINQ in a multi-threaded application, be aware to always materialize the queries before leaving the protected regions.
InvalidOperationException-
An InvalidOperationException has occurred. It reports a "collection was modified" in a foreach-loop
Use break statement, Once the object is removed.
ex:
ArrayList list = new ArrayList();
foreach (var item in list)
{
if(condition)
{
list.remove(item);
break;
}
}
Actually the problem seems to me that you are removing elements from the list and expecting to continue to read the list as if nothing had happened.
What you really need to do is to start from the end and back to the begining. Even if you remove elements from the list you will be able to continue reading it.
I had the same issue, and it was solved when I used a for loop instead of 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);
}
I've seen many options for this but to me this one was the best.
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected)
collection.Add(item);
}
Then simply loop through the collection.
Be aware that a ListItemCollection can contain duplicates. By default there is nothing preventing duplicates being added to the collection. To avoid duplicates you can do this:
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected && !collection.Contains(item))
collection.Add(item);
}
This way should cover a situation of concurrency when the function is called again while is still executing (and items need used only once):
while (list.Count > 0)
{
string Item = list[0];
list.RemoveAt(0);
// do here what you need to do with item
}
If the function get called while is still executing items will not reiterate from the first again as they get deleted as soon as they get used.
Should not affect performance much for small lists.
There is one link where it elaborated very well & solution is also given.
Try it if you got proper solution please post here so other can understand.
Given solution is ok then like the post so other can try these solution.
for you reference original link :-
https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/
When we use .Net Serialization classes to serialize an object where its definition contains an Enumerable type, i.e.
collection, you will be easily getting InvalidOperationException saying "Collection was modified;
enumeration operation may not execute" where your coding is under multi-thread scenarios.
The bottom cause is that serialization classes will iterate through collection via enumerator, as such,
problem goes to trying to iterate through a collection while modifying it.
First solution, we can simply use lock as a synchronization solution to ensure that
the operation to the List object can only be executed from one thread at a time.
Obviously, you will get performance penalty that
if you want to serialize a collection of that object, then for each of them, the lock will be applied.
Well, .Net 4.0 which makes dealing with multi-threading scenarios handy.
for this serializing Collection field problem, I found we can just take benefit from ConcurrentQueue(Check MSDN)class,
which is a thread-safe and FIFO collection and makes code lock-free.
Using this class, in its simplicity, the stuff you need to modify for your code are replacing Collection type with it,
use Enqueue to add an element to the end of ConcurrentQueue, remove those lock code.
Or, if the scenario you are working on do require collection stuff like List, you will need a few more code to adapt ConcurrentQueue into your fields.
BTW, ConcurrentQueue doesnât have a Clear method due to underlying algorithm which doesnât permit atomically clearing of the collection.
so you have to do it yourself, the fastest way is to re-create a new empty ConcurrentQueue for a replacement.
Here is a specific scenario that warrants a specialized approach:
The Dictionary is enumerated frequently.
The Dictionary is modified infrequently.
In this scenario creating a copy of the Dictionary (or the Dictionary.Values) before every enumeration can be quite costly. My idea about solving this problem is to reuse the same cached copy in multiple enumerations, and watch an IEnumerator of the original Dictionary for exceptions. The enumerator will be cached along with the copied data, and interrogated before starting a new enumeration. In case of an exception the cached copy will be discarded, and a new one will be created. Here is my implementation of this idea:
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);
}
Usage example:
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)
{
//...
}
Unfortunately this idea cannot be used currently with the class Dictionary in .NET Core 3.0, because this class does not throw a Collection was modified exception when enumerated and the methods Remove and Clear are invoked. All other containers I checked are behaving consistently. I checked systematically these classes:
List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V> and SortedDictionary<T,V>. Only the two aforementioned methods of the Dictionary class in .NET Core are not invalidating the enumeration.
Update: I fixed the above problem by comparing also the lengths of the cached and the original collection. This fix assumes that the dictionary will be passed directly as an argument to the EnumerableSnapshot's constructor, and its identity will not be hidden by (for example) a projection like: dictionary.Select(e => e).ΤοEnumerableSnapshot().
Important: The above class is not thread safe. It is intended to be used from code running exclusively in a single thread.
You can copy subscribers dictionary object to a same type of temporary dictionary object and then iterate the temporary dictionary object using foreach loop.
So a different way to solve this problem would be instead of removing the elements create a new dictionary and only add the elements you didnt want to remove then replace the original dictionary with the new one. I don't think this is too much of an efficiency problem because it does not increase the number of times you iterate over the structure.

How use LINQ to remove maximum items in a Dictionary<TKey, TValue>? [duplicate]

I can't get to the bottom of this error, because when the debugger is attached, it does not seem to occur.
Collection was modified; enumeration operation may not execute
Below is the code.
This is a WCF server in a Windows service. The method NotifySubscribers() is called by the service whenever there is a data event (at random intervals, but not very often - about 800 times per day).
When a Windows Forms client subscribes, the subscriber ID is added to the subscribers dictionary, and when the client unsubscribes, it is deleted from the dictionary. The error happens when (or after) a client unsubscribes. It appears that the next time the NotifySubscribers() method is called, the foreach() loop fails with the error in the subject line. The method writes the error into the application log as shown in the code below. When a debugger is attached and a client unsubscribes, the code executes fine.
Do you see a problem with this code? Do I need to make the dictionary thread-safe?
[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);
}
}
}
What's likely happening is that SignalData is indirectly changing the subscribers dictionary under the hood during the loop and leading to that message. You can verify this by changing
foreach(Subscriber s in subscribers.Values)
To
foreach(Subscriber s in subscribers.Values.ToList())
If I'm right, the problem will disappear.
Calling subscribers.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach. Nothing else has access to this list (it doesn't even have a variable name!), so nothing can modify it inside the loop.
When a subscriber unsubscribes you are changing contents of the collection of Subscribers during enumeration.
There are several ways to fix this, one being changing the for loop to use an explicit .ToList():
public void NotifySubscribers(DataRecord sr)
{
foreach(Subscriber s in subscribers.Values.ToList())
{
^^^^^^^^^
...
A more efficient way, in my opinion, is to have another list that you declare that you put anything that is "to be removed" into. Then after you finish your main loop (without the .ToList()), you do another loop over the "to be removed" list, removing each entry as it happens. So in your class you add:
private List<Guid> toBeRemoved = new List<Guid>();
Then you change it to:
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 );
}
This will not only solve your problem, it will prevent you from having to keep creating a list from your dictionary, which is expensive if there are a lot of subscribers in there. Assuming the list of subscribers to be removed on any given iteration is lower than the total number in the list, this should be faster. But of course feel free to profile it to be sure that's the case if there's any doubt in your specific usage situation.
Why this error?
In general .Net collections do not support being enumerated and modified at the same time. If you try to modify the collection list during enumeration, it raises an exception. So the issue behind this error is, we can not modify the list/dictionary while we are looping through the same.
One of the solutions
If we iterate a dictionary using a list of its keys, in parallel we can modify the dictionary object, as we are iterating through the key-collection and
not the dictionary(and iterating its key collection).
Example
//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;
}
Here is a blog post about this solution.
And for a deep dive in StackOverflow: Why this error occurs?
Okay so what helped me was iterating backwards. I was trying to remove an entry from a list but iterating upwards and it screwed up the loop because the entry didn't exist anymore:
for (int x = myList.Count - 1; x > -1; x--)
{
myList.RemoveAt(x);
}
The accepted answer is imprecise and incorrect in the worst case . If changes are made during ToList(), you can still end up with an error. Besides lock, which performance and thread-safety needs to be taken into consideration if you have a public member, a proper solution can be using immutable types.
In general, an immutable type means that you can't change the state of it once created.
So your code should look like:
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
}
}
//.........
}
This can be especially useful if you have a public member. Other classes can always foreach on the immutable types without worrying about the collection being modified.
I want to point out other case not reflected in any of the answers. I have a Dictionary<Tkey,TValue> shared in a multi threaded app, which uses a ReaderWriterLockSlim to protect the read and write operations. This is a reading method that throws the exception:
public IEnumerable<Data> GetInfo()
{
List<Data> info = null;
_cacheLock.EnterReadLock();
try
{
info = _cache.Values.SelectMany(ce => ce.Data); // Ad .Tolist() to avoid exc.
}
finally
{
_cacheLock.ExitReadLock();
}
return info;
}
In general, it works fine, but from time to time I get the exception. The problem is a subtlety of LINQ: this code returns an IEnumerable<Info>, which is still not enumerated after leaving the section protected by the lock. So, it can be changed by other threads before being enumerated, leading to the exception. The solution is to force the enumeration, for example with .ToList() as shown in the comment. In this way, the enumerable is already enumerated before leaving the protected section.
So, if using LINQ in a multi-threaded application, be aware to always materialize the queries before leaving the protected regions.
InvalidOperationException-
An InvalidOperationException has occurred. It reports a "collection was modified" in a foreach-loop
Use break statement, Once the object is removed.
ex:
ArrayList list = new ArrayList();
foreach (var item in list)
{
if(condition)
{
list.remove(item);
break;
}
}
Actually the problem seems to me that you are removing elements from the list and expecting to continue to read the list as if nothing had happened.
What you really need to do is to start from the end and back to the begining. Even if you remove elements from the list you will be able to continue reading it.
I had the same issue, and it was solved when I used a for loop instead of 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);
}
I've seen many options for this but to me this one was the best.
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected)
collection.Add(item);
}
Then simply loop through the collection.
Be aware that a ListItemCollection can contain duplicates. By default there is nothing preventing duplicates being added to the collection. To avoid duplicates you can do this:
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected && !collection.Contains(item))
collection.Add(item);
}
This way should cover a situation of concurrency when the function is called again while is still executing (and items need used only once):
while (list.Count > 0)
{
string Item = list[0];
list.RemoveAt(0);
// do here what you need to do with item
}
If the function get called while is still executing items will not reiterate from the first again as they get deleted as soon as they get used.
Should not affect performance much for small lists.
There is one link where it elaborated very well & solution is also given.
Try it if you got proper solution please post here so other can understand.
Given solution is ok then like the post so other can try these solution.
for you reference original link :-
https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/
When we use .Net Serialization classes to serialize an object where its definition contains an Enumerable type, i.e.
collection, you will be easily getting InvalidOperationException saying "Collection was modified;
enumeration operation may not execute" where your coding is under multi-thread scenarios.
The bottom cause is that serialization classes will iterate through collection via enumerator, as such,
problem goes to trying to iterate through a collection while modifying it.
First solution, we can simply use lock as a synchronization solution to ensure that
the operation to the List object can only be executed from one thread at a time.
Obviously, you will get performance penalty that
if you want to serialize a collection of that object, then for each of them, the lock will be applied.
Well, .Net 4.0 which makes dealing with multi-threading scenarios handy.
for this serializing Collection field problem, I found we can just take benefit from ConcurrentQueue(Check MSDN)class,
which is a thread-safe and FIFO collection and makes code lock-free.
Using this class, in its simplicity, the stuff you need to modify for your code are replacing Collection type with it,
use Enqueue to add an element to the end of ConcurrentQueue, remove those lock code.
Or, if the scenario you are working on do require collection stuff like List, you will need a few more code to adapt ConcurrentQueue into your fields.
BTW, ConcurrentQueue doesnât have a Clear method due to underlying algorithm which doesnât permit atomically clearing of the collection.
so you have to do it yourself, the fastest way is to re-create a new empty ConcurrentQueue for a replacement.
Here is a specific scenario that warrants a specialized approach:
The Dictionary is enumerated frequently.
The Dictionary is modified infrequently.
In this scenario creating a copy of the Dictionary (or the Dictionary.Values) before every enumeration can be quite costly. My idea about solving this problem is to reuse the same cached copy in multiple enumerations, and watch an IEnumerator of the original Dictionary for exceptions. The enumerator will be cached along with the copied data, and interrogated before starting a new enumeration. In case of an exception the cached copy will be discarded, and a new one will be created. Here is my implementation of this idea:
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);
}
Usage example:
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)
{
//...
}
Unfortunately this idea cannot be used currently with the class Dictionary in .NET Core 3.0, because this class does not throw a Collection was modified exception when enumerated and the methods Remove and Clear are invoked. All other containers I checked are behaving consistently. I checked systematically these classes:
List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V> and SortedDictionary<T,V>. Only the two aforementioned methods of the Dictionary class in .NET Core are not invalidating the enumeration.
Update: I fixed the above problem by comparing also the lengths of the cached and the original collection. This fix assumes that the dictionary will be passed directly as an argument to the EnumerableSnapshot's constructor, and its identity will not be hidden by (for example) a projection like: dictionary.Select(e => e).ΤοEnumerableSnapshot().
Important: The above class is not thread safe. It is intended to be used from code running exclusively in a single thread.
You can copy subscribers dictionary object to a same type of temporary dictionary object and then iterate the temporary dictionary object using foreach loop.
So a different way to solve this problem would be instead of removing the elements create a new dictionary and only add the elements you didnt want to remove then replace the original dictionary with the new one. I don't think this is too much of an efficiency problem because it does not increase the number of times you iterate over the structure.

Collection was modified; enumeration operation may not execute

I can't get to the bottom of this error, because when the debugger is attached, it does not seem to occur.
Collection was modified; enumeration operation may not execute
Below is the code.
This is a WCF server in a Windows service. The method NotifySubscribers() is called by the service whenever there is a data event (at random intervals, but not very often - about 800 times per day).
When a Windows Forms client subscribes, the subscriber ID is added to the subscribers dictionary, and when the client unsubscribes, it is deleted from the dictionary. The error happens when (or after) a client unsubscribes. It appears that the next time the NotifySubscribers() method is called, the foreach() loop fails with the error in the subject line. The method writes the error into the application log as shown in the code below. When a debugger is attached and a client unsubscribes, the code executes fine.
Do you see a problem with this code? Do I need to make the dictionary thread-safe?
[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);
}
}
}
What's likely happening is that SignalData is indirectly changing the subscribers dictionary under the hood during the loop and leading to that message. You can verify this by changing
foreach(Subscriber s in subscribers.Values)
To
foreach(Subscriber s in subscribers.Values.ToList())
If I'm right, the problem will disappear.
Calling subscribers.Values.ToList() copies the values of subscribers.Values to a separate list at the start of the foreach. Nothing else has access to this list (it doesn't even have a variable name!), so nothing can modify it inside the loop.
When a subscriber unsubscribes you are changing contents of the collection of Subscribers during enumeration.
There are several ways to fix this, one being changing the for loop to use an explicit .ToList():
public void NotifySubscribers(DataRecord sr)
{
foreach(Subscriber s in subscribers.Values.ToList())
{
^^^^^^^^^
...
A more efficient way, in my opinion, is to have another list that you declare that you put anything that is "to be removed" into. Then after you finish your main loop (without the .ToList()), you do another loop over the "to be removed" list, removing each entry as it happens. So in your class you add:
private List<Guid> toBeRemoved = new List<Guid>();
Then you change it to:
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 );
}
This will not only solve your problem, it will prevent you from having to keep creating a list from your dictionary, which is expensive if there are a lot of subscribers in there. Assuming the list of subscribers to be removed on any given iteration is lower than the total number in the list, this should be faster. But of course feel free to profile it to be sure that's the case if there's any doubt in your specific usage situation.
Why this error?
In general .Net collections do not support being enumerated and modified at the same time. If you try to modify the collection list during enumeration, it raises an exception. So the issue behind this error is, we can not modify the list/dictionary while we are looping through the same.
One of the solutions
If we iterate a dictionary using a list of its keys, in parallel we can modify the dictionary object, as we are iterating through the key-collection and
not the dictionary(and iterating its key collection).
Example
//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;
}
Here is a blog post about this solution.
And for a deep dive in StackOverflow: Why this error occurs?
Okay so what helped me was iterating backwards. I was trying to remove an entry from a list but iterating upwards and it screwed up the loop because the entry didn't exist anymore:
for (int x = myList.Count - 1; x > -1; x--)
{
myList.RemoveAt(x);
}
The accepted answer is imprecise and incorrect in the worst case . If changes are made during ToList(), you can still end up with an error. Besides lock, which performance and thread-safety needs to be taken into consideration if you have a public member, a proper solution can be using immutable types.
In general, an immutable type means that you can't change the state of it once created.
So your code should look like:
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
}
}
//.........
}
This can be especially useful if you have a public member. Other classes can always foreach on the immutable types without worrying about the collection being modified.
I want to point out other case not reflected in any of the answers. I have a Dictionary<Tkey,TValue> shared in a multi threaded app, which uses a ReaderWriterLockSlim to protect the read and write operations. This is a reading method that throws the exception:
public IEnumerable<Data> GetInfo()
{
List<Data> info = null;
_cacheLock.EnterReadLock();
try
{
info = _cache.Values.SelectMany(ce => ce.Data); // Ad .Tolist() to avoid exc.
}
finally
{
_cacheLock.ExitReadLock();
}
return info;
}
In general, it works fine, but from time to time I get the exception. The problem is a subtlety of LINQ: this code returns an IEnumerable<Info>, which is still not enumerated after leaving the section protected by the lock. So, it can be changed by other threads before being enumerated, leading to the exception. The solution is to force the enumeration, for example with .ToList() as shown in the comment. In this way, the enumerable is already enumerated before leaving the protected section.
So, if using LINQ in a multi-threaded application, be aware to always materialize the queries before leaving the protected regions.
InvalidOperationException-
An InvalidOperationException has occurred. It reports a "collection was modified" in a foreach-loop
Use break statement, Once the object is removed.
ex:
ArrayList list = new ArrayList();
foreach (var item in list)
{
if(condition)
{
list.remove(item);
break;
}
}
Actually the problem seems to me that you are removing elements from the list and expecting to continue to read the list as if nothing had happened.
What you really need to do is to start from the end and back to the begining. Even if you remove elements from the list you will be able to continue reading it.
I had the same issue, and it was solved when I used a for loop instead of 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);
}
I've seen many options for this but to me this one was the best.
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected)
collection.Add(item);
}
Then simply loop through the collection.
Be aware that a ListItemCollection can contain duplicates. By default there is nothing preventing duplicates being added to the collection. To avoid duplicates you can do this:
ListItemCollection collection = new ListItemCollection();
foreach (ListItem item in ListBox1.Items)
{
if (item.Selected && !collection.Contains(item))
collection.Add(item);
}
This way should cover a situation of concurrency when the function is called again while is still executing (and items need used only once):
while (list.Count > 0)
{
string Item = list[0];
list.RemoveAt(0);
// do here what you need to do with item
}
If the function get called while is still executing items will not reiterate from the first again as they get deleted as soon as they get used.
Should not affect performance much for small lists.
There is one link where it elaborated very well & solution is also given.
Try it if you got proper solution please post here so other can understand.
Given solution is ok then like the post so other can try these solution.
for you reference original link :-
https://bensonxion.wordpress.com/2012/05/07/serializing-an-ienumerable-produces-collection-was-modified-enumeration-operation-may-not-execute/
When we use .Net Serialization classes to serialize an object where its definition contains an Enumerable type, i.e.
collection, you will be easily getting InvalidOperationException saying "Collection was modified;
enumeration operation may not execute" where your coding is under multi-thread scenarios.
The bottom cause is that serialization classes will iterate through collection via enumerator, as such,
problem goes to trying to iterate through a collection while modifying it.
First solution, we can simply use lock as a synchronization solution to ensure that
the operation to the List object can only be executed from one thread at a time.
Obviously, you will get performance penalty that
if you want to serialize a collection of that object, then for each of them, the lock will be applied.
Well, .Net 4.0 which makes dealing with multi-threading scenarios handy.
for this serializing Collection field problem, I found we can just take benefit from ConcurrentQueue(Check MSDN)class,
which is a thread-safe and FIFO collection and makes code lock-free.
Using this class, in its simplicity, the stuff you need to modify for your code are replacing Collection type with it,
use Enqueue to add an element to the end of ConcurrentQueue, remove those lock code.
Or, if the scenario you are working on do require collection stuff like List, you will need a few more code to adapt ConcurrentQueue into your fields.
BTW, ConcurrentQueue doesnât have a Clear method due to underlying algorithm which doesnât permit atomically clearing of the collection.
so you have to do it yourself, the fastest way is to re-create a new empty ConcurrentQueue for a replacement.
Here is a specific scenario that warrants a specialized approach:
The Dictionary is enumerated frequently.
The Dictionary is modified infrequently.
In this scenario creating a copy of the Dictionary (or the Dictionary.Values) before every enumeration can be quite costly. My idea about solving this problem is to reuse the same cached copy in multiple enumerations, and watch an IEnumerator of the original Dictionary for exceptions. The enumerator will be cached along with the copied data, and interrogated before starting a new enumeration. In case of an exception the cached copy will be discarded, and a new one will be created. Here is my implementation of this idea:
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);
}
Usage example:
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)
{
//...
}
Unfortunately this idea cannot be used currently with the class Dictionary in .NET Core 3.0, because this class does not throw a Collection was modified exception when enumerated and the methods Remove and Clear are invoked. All other containers I checked are behaving consistently. I checked systematically these classes:
List<T>, Collection<T>, ObservableCollection<T>, HashSet<T>, SortedSet<T>, Dictionary<T,V> and SortedDictionary<T,V>. Only the two aforementioned methods of the Dictionary class in .NET Core are not invalidating the enumeration.
Update: I fixed the above problem by comparing also the lengths of the cached and the original collection. This fix assumes that the dictionary will be passed directly as an argument to the EnumerableSnapshot's constructor, and its identity will not be hidden by (for example) a projection like: dictionary.Select(e => e).ΤοEnumerableSnapshot().
Important: The above class is not thread safe. It is intended to be used from code running exclusively in a single thread.
You can copy subscribers dictionary object to a same type of temporary dictionary object and then iterate the temporary dictionary object using foreach loop.
So a different way to solve this problem would be instead of removing the elements create a new dictionary and only add the elements you didnt want to remove then replace the original dictionary with the new one. I don't think this is too much of an efficiency problem because it does not increase the number of times you iterate over the structure.

Categories

Resources