Reputation: 397
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.
Upvotes: 1
Views: 92
Reputation: 397
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)
Upvotes: 1
Reputation: 2480
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--)
{
}
Upvotes: 0
Reputation: 3153
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.
Upvotes: 1
Reputation: 35822
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.
Upvotes: 0