I have a dictionary that is being used in two threads, one is sending out UDP packets, one is receiving them. Both are keeping a common collection to see count outgoing and returning packets and hopefully keeping them at 0 difference :)
Now I'm iterating through the dictionary to update values and after iteration it errors. I do have a lock object in place, how could I solve this?
First Thread:
lock (retryListLock)
{
// loop through all known devices in the device list to build a counter if the device still lives
foreach(string key in retryList.Keys)
{
retryList[key] += 1;
if (retryList[key] > Retries)
{
DiscoveredDevice device = Devices.Find(d => d.SerialNo == key);
if (device != null)
{
OnDeviceRemoved(device);
Devices.Remove(device);
retryList.Remove(key);
}
}
}
}
Second Thread:
lock (retryListLock)
{
if (retryList.ContainsKey(frame.SerialNo))
retryList[frame.SerialNo] = 0;
else
retryList.Add(frame.SerialNo, 0);
}
I'm only getting the error after the first thread adds +1 to the value of that item, in the second iteration it errors out:
the collection has changed. enumeration operation may not execute (translated from Dutch)
How can I solve this? Obviously the Dictionary is the easiest to use for me in this case.
The problem is that you cannot change the dictionary in an iterator/foreach
foreach(string key in retryList.Keys)
{
retryList[key] += 1; // <-- The error happens here ! Do not alter the Dictionary during an iteration
if (retryList[key] > Retries)
{
DiscoveredDevice device = Devices.Find(d => d.SerialNo == key);
if (device != null)
{
OnDeviceRemoved(device);
Devices.Remove(device);
retryList.Remove(key); // <-- The error could also happen here ! Do not alter the Dictionary during an iteration
}
}
}
I found this question on stackoverflow which might help you
How to iterate through Dictionary and change values?
Here we find a statement from MSDN
The foreach statement is a wrapper around the enumerator, which allows
only reading from the collection, not writing to it.
With thanks to Bongo, taking the keys into a second list for iteration solved it:
List<string> Keys = new List<string>(retryList.Keys);
foreach(string key in Keys)
The error has nothing to do with the locks and multithreading.
Your enumerator (what foreach are using) is invalidated when you modify the very same data structure (the dictionary) on what the enumerator enumerates in the loop.
Solution:
You must first loop say with foreach, and remember practically in a list what you should remove. Then in a separate loop on the remembered keys, remove they from the dictionary.
This might be useful to you. Concurrent Dictionary
It's thread safe
Also change the for each to a reverse for loop
Something like this.
for (int x = max; x>=1; x--)
{
}
Related
Let's say i have a update function like that:
for (int i = 0; i < players.Count; i++)
{
Debug.Log(players[i].name);
}
This list is being used in Update() so if i remove any of list items from another script/function it will just throw a error because list size is changed while loop is going on. How do i fix this?
This bug bug pops up everywhere in programming where you are working with some collection that might modify the collection you are working with.
In my experience there are two options:
Take a copy of the collection and work with the copy, if it is okay to show out of date information.
Handle any errors that occur from the collection being modified and restart your logic using the latest collection.
Option 1: Work with a copy:
Before accessing the collection, take a copy of what is in memory. For example, use.ToList() or .ToArray()
var playersCopy = players.ToList();
for (int i = 0; i < playersCopy .Count; i++)
{
Debug.Log(playersCopy [i].name);
}
Using this approach you have a copy in memory that cannot be modified, even thought that copy can possible be stale or out of date.
2). If using a stale copy is not acceptable, then you need to handle an exceptions that occur from the collection being modified while you are iterating through it.
For example, you can put access to the collection inside of a try catch and retry your logic if the collection was modified.
bool success = false;
while(!success)
{
try {
{
for (int i = 0; i < players.Count; i++)
{
Debug.Log(players[i].name);
}
success=true; // loop completed without error
}
catch { }
}
Note the later is a simplified example and depending on your circumstance you might want to do different actions with the caught error.
Also note, that latter is not recommended if there are side effects For example, Debug.Log might already have printed out a stale player.
This has its own solutions, EG: Save the player names to a list first and then print out the in memory list or again use custom logic (for example, track which players are no longer in the list and print a message saying they left and continue with the rest of the list).
Still the approach is the same, use an in memory copy or detect the changes in real time and handle them.
Fortunately, based on the information you supplied, this won't throw an error.
In a standard scenario, the Update method will be called and complete before the next Update method on the following script is called. The main Unity worker thread isn't being called from anywhere else, so one script wouldn't interrupt an Update method call mid function.
Now, if you had said you were using co-routines, and had yielded during a 'foreach' loop, then in that case, you might have a scenario whereby the enumerator threw an error, because you could have modified the underlying enumerable (players) from somewhere else in this script, or another.
Another way to put it, this is bad:
public class Base : MonoBehaviour
{
List<int> list;
void Start ( )
{
list = new List<int> { 1, 2, 3, 4 };
StartCoroutine ( ForEachTest ( ) );
list.Add ( 5 );
}
IEnumerator ForEachTest ( )
{
foreach ( var item in list )
{
Debug.Log ( $"Item value : {item}" );
yield return null;
}
}
}
Would result in:
InvalidOperationException: Collection was modified; enumeration
operation may not execute.
If you take a look at the code for the enumerator, you can see why an Exception is thrown.
public bool MoveNext()
{
List<T> list = this.list;
if (version == list._version && (uint)index < (uint)list._size)
{
current = list._items[index];
index++;
return true;
}
return MoveNextRare();
}
private bool MoveNextRare()
{
if (version != list._version)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
index = list._size + 1;
current = default(T);
return false;
}
When a List (as suggested by your use of Count, not Length) is modified, the enumerator sees that the collection's version has been modified, which invalidates the enumerator.
Note, that if you don't use an enumerator, and simply use a for loop that is checking the list's Count property, you won't run into this problem.
I need to copy one record from one dictionary to another, only one. I have tried the below, It doesn't copy one but everything due to that its in the loop. And I need to check if the key is not already copied over to the second dictionary that's why I looped over the first dictionary.
foreach (int key in firstDict.Keys)
{
if (!secondDict.ContainsKey(key))
secondDict.Add(key, firstDict[key]);
else {break;}
}
Please help, should you require more info please let me know.
Try this:
foreach (int key in firstDict.Keys)
{
if (!secondDict.ContainsKey(key))
{
secondDict.Add(key, firstDict[key]);
break;
}
}
A tiny bit more efficient than the others answers already posted (saves one dictionary lookup).
foreach(var kvp in firstDict)
{
if(!secondDict.ContainsKey(kvp.Key))
{
secondDict[kvp.Key] = kvp.Value;
break;
}
}
And if you're familiar with linq you may want to avoid the whole foreach ... break pattern. That pattern is pretty ugly and more importantly confusing: it looks like you want to copy everything when you really want to copy just one. Your question itself is proof of how confusing it is: if you don't get a pattern that should be simple right, that probably means the pattern is not very good. The following will do the work, and it's crystal clear it will only add ONE (at most) entry to the second dictionary.
var missingEntry = firstDict
// cast beacause KeyValuePair is a struct and I want to detect the case where there is no missing entry
.Cast<KeyValuePair<TKey,TValue>?>()
.FirstOrDefault(kvp => !secondDict.ContainsKey(kvp.Key));
// check whether there truely is a missing entry.
if (missingEntry != null)
{
secondDict[missingEntry.Key] = missingEntry.Value;
}
remove the else clause
foreach (int key in firstDict.Keys)
{
if (!secondDict.ContainsKey(key)) {
secondDict.Add(key, firstDict[key]);
break;
}
}
I suppose you have the key of that row in a certain variable that i will call key. here for example I'll set its value to 3.
int key = 3;
if (!secondDict.ContainsKey(key))
secondDict.Add(key, firstDict[key]);
EDIT:
If you want only any single entry that is not present in present in secondDict:
foreach (KeyValuePair<int, MyValueType> kvp in firstDict) //Replace MyValueType with the real value type in firstDict
{
if (!secondDict.ContainsKey(kvp.Key))
{
secondDict.Add(kvp.Key, kvp.Value);
break;
}
}
I am trying to remove object while I am iterating through Collection. But I am getting exception. How can I achieve this?
Here is my code :
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(gem.Key); // I can't do this here, then How can I do?
OnGemCollected(gem.Value, Player);
}
}
foreach is designed for iterating over a collection without modifing it.
To remove items from a collection while iterating over it use a for loop from the end to the start of it.
for(int i = gems.Count - 1; i >=0 ; i--)
{
gems[i].Value.Update(gameTime);
if (gems[i].Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
Gem gem = gems[i];
gems.RemoveAt(i); // Assuming it's a List<Gem>
OnGemCollected(gem.Value, Player);
}
}
If it's a dictionary<string, Gem> for example, you could iterate like this:
foreach(string s in gems.Keys.ToList())
{
if(gems[s].BoundingCircle.Intersects(Player.BoundingRectangle))
{
gems.Remove(s);
}
}
The easiest way is to do what #IV4 suggested:
foreach (var gem in gems.ToList())
The ToList() will convert the Dictionary to a list of KeyValuePair, so it will work fine.
The only time you wouldn't want to do it that way is if you have a big dictionary from which you are only removing relatively few items and you want to reduce memory use.
Only in that case would you want to use one of the following approaches:
Make a list of the keys as you find them, then have a separate loop to remove the items:
List<KeyType> keysToRemove = new List<KeyType>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
{
OnGemCollected(gem.Value, Player);
keysToRemove.Add(gem.Key);
}
}
foreach (var key in keysToRemove)
gems.Remove(key);
(Where KeyType is the type of key you're using. Substitute the correct type!)
Alternatively, if it is important that the gem is removed before calling OnGemCollected(), then (with key type TKey and value type TValue) do it like this:
var itemsToRemove = new List<KeyValuePair<TKey, TValue>>();
foreach (var gem in gems)
{
gem.Value.Update(gameTime);
if (gem.Value.BoundingCircle.Intersects(Player.BoundingRectangle))
itemsToRemove.Add(gem);
}
foreach (var item in itemsToRemove)
{
gems.Remove(item.Key);
OnGemCollected(item.Value, Player);
}
As the other answers say, a foreach is designed purely for iterating over a collection without modifying it as per the documenation:
The foreach statement is used to iterate through the collection to get
the desired information, but should not be used to change the contents
of the collection to avoid unpredictable side effects.
in order to do this you would need to use a for loop (storing the items of the collection you need to remove) and remove them from the collection afterwards.
However if you are using a List<T> you could do this:
lines.RemoveAll(line => line.FullfilsCertainConditions());
After going through all the answers, and being equally good. I faced a challenge where I had to modify a List and what I ended up doing worked quite well for me. So just in case anyone finds it useful. Can someone provide me feedback on how efficient it might be.
Action removeFromList;
foreach(var value in listOfValues){
if(whatever condition to remove is){
removeFromList+=()=>listOfValues.remove(value);
}
}
removeFromList?.Invoke();
removeFromList = null;
You should use the for loop instead of the foreach loop. Please refer here
Collections support foreach statement using Enumarator. Enumerators can be used to read the data in the collection, but they cannot be used to modify the underlying collection. If changes are made to the collection, such as adding, modifying, or deleting elements, the enumerator is irrecoverably invalidated and the next call to MoveNext or Reset throws an InvalidOperationException.
Use for loop for collection modifying.
Look at this csharp code, and see if you can tell why I need to exit the loop after I found and deleted an item from the list. The idea is to go through a node's list of neighbors, and see if a Node n exists there, then delete that neighbor:
internal void RemoveDirected(Node n)
{
foreach (EdgeToNeighbor etn in this.Neighbors)
{
if (etn.Neighbor.Key == n.Key)
{
RemoveDirected(etn);
break;
}
}
}
internal void RemoveDirected(EdgeToNeighbor e)
{
Neighbors.Remove(e);
}
.
.
.
// Removes EdgeToNeighbor instance from AdjacencyList
protected internal virtual void Remove(EdgeToNeighbor e)
{
base.InnerList.Remove(e);
}
Notice how I have a "break" after the RemoveDirected call in the first method.
I've found that if I didn't exit after the RemoveDirected
it would go on forever in the foreach loop. I suppose it must have
something to do with the way foreach works. If you modify the list
that foreach is working on, it gets confuse and loops forever.
Have you seen this type of thing, and what are other options to use rather than using break?
Of course, I could place the node that I've found in a local variable, then break from the loop, and
delete it outside of the loop. But I was thinking, may be there are better ways to do this
in csharp.
When you iterate a .NET collection using its iterator, you must not modify that collection. If you do, you are asking for trouble.
You should defer the deletion instead of deleting right in the foreach loop. For example, you can collect everything you need to delete in a list, and then delete it outside of foreach.
var toDelete = this.Neighbors.Where(etn => etn.Neighbor.Key == n.Key).ToList();
foreach (var e in toDelete) {
Neighbors.Remove(e);
}
You definitely don't want to use an iterator if you are removing items. One option is to change it to downward counting loop:
for (int nI = this.Neighbors.Count; nI >= 0; nI--)
{
var etn = this.Neighbors[nI];
if (etn.Neighbor.Key == n.Key)
{
RemoveDirected(n);
}
}
You could also collect the keys or items to be deleted in a collection within the loop and then perform the deletion once you have completed your iteration.
However, if you are only removing a single item and the items in the collection are keyed somehow, then you shouldn't actually need any loop. Just test the existence of the key or item in the collection and, if it is there, just remove it.
You could get the number of nodes and then check and delete from highest to lowest in a for loop that way you avoid looking for an item that no longer exists.
My windows phone 7 silverlight app before placing pushpins on a map layer removes any that are previously there.
I was doing this in a foreach loop as follows:
//Clear previous pins
try
{
foreach (UIElement p in PushPinLayer.Children)
{
if(p.GetType() == typeof(Pushpin))
{
PushPinLayer.Children.Remove(p);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
//TODO: For some reason the foreach loop above causes an invalid Operation exception.
//Cathing the error here until I can work out why it is happening.
}
This code removes any pushpins as required but after the last loop would throw an exception "Invalid Operation" I re-wrote it as a for loop:
for (int i = 0; i < PushPinLayer.Children.Count; i++)
{
if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
{
PushPinLayer.Children.RemoveAt(i);
}
}
Which works fine, however I can't see why the foreach is throwing an error.
This is very normal,
You cannot remove items from a list which you still use in the foreach list.
better then removing the item would be to create a new list, and every time it is not a pushpin type, add the object to the new list.
This way the original list is not altered and you won't get an exception.
I find it strange that the for loop works, but if it does, it would mean that the way they are itterated is different. The for loop will be copied to another memory location and used for the for loop so that the original one, in which you remove items, is not used anymore by the for loop. The foreach loop will get parameters from the list, the you remove items, so the list and the parameters become concurrent.
Your foreach loop uses and Enumerator to iterate the objects in your collection. When you delete an object from the collection the Enumerator is no longer valid as it references object that no longer exist. This causes a InvalidOperationException
The best way to remove them is using a for loop and even better doing it in reverse.
for (int i = PushPinLayer.Children.Count - 1; i >= 0 ; i--)
{
if (PushPinLayer.Children[i].GetType() == typeof(Pushpin))
{
PushPinLayer.Children.RemoveAt(i);
}
}
This will ensure that as the items are removed your Index i does not exceed the number of items in your collection.
Since others have already answered your question, I will only comment on your usage of xxx.GetType() == typeof(Pushpin). You could check whether something is a pushpin by simply using the C# reserved keyword is. e.g.:
if (p is Pushpin) {...}
When you change the content of the collection, the enumerator used in the foreach loop becomes invalid. You can't change a collection while you enumerate it.
Here's a workaround:
List<UIElement> toRemove = new List<UIElement>();
foreach (UIElement p in PushPinLayer.Children)
{
if(p.GetType() == typeof(Pushpin))
{
toRemove.Add(p);
}
}
foreach(UIElement p in toRemove)
{
PushPinLayer.Children.Remove(p);
}
Alternatively, you could use the RemoveAll method, which takes a predicate as a parameter:
PushPinLayer.Children.RemoveAll(p => p is Pushpin);
Others have already given you the reason for the problem so I just thought I'd post a LINQ version of a work around
var toRemove = PushPinLayer.Children.OfType<Pushpin>().ToList();
// since toRemove is a separate collection, it's safe to do this now:
foreach (var child in toRemove)
PushPinLayer.Children.Remove(child)
You must not change the collection while foreaching through it.