InvalidOperationException: collection has been changed - although collection lock

I have a synchronized hash table from which I regularly delete some entries. This code executes multiple threads. Therefore, I block the entire foreach, but sometimes I get InvalidOperationException: Collection has been changed ... in Hashtable.HashtableEnumerator.MoveNext () - that is, in the foreach loop. What am I doing wrong? Is blocking enough?

private static readonly Hashtable sessionsTimeoutData = Hashtable.Synchronized(new Hashtable(5000)); 

private static void ClearTimedoutSessions() { List keysToRemove = new List(); long now = DateTime.Now.Ticks; lock (sessionsTimeoutData) { TimeoutData timeoutData; foreach (DictionaryEntry entry in sessionsTimeoutData) { timeoutData = (TimeoutData)entry.Value; if (now - timeoutData.LastAccessTime > timeoutData.UserTimeoutTicks) keysToRemove.Add((ulong)entry.Key); } } foreach (ulong key in keysToRemove) sessionsTimeoutData.Remove(key); }

+7
source share
3 answers

You want to block using SyncRoot , which is the object that the synchronized Hashtable methods will block:

 lock (sessionsTimeoutData.SyncRoot) { // ... } 

See http://msdn.microsoft.com/en-us/library/system.collections.hashtable.synchronized.aspx :

Listing through a collection is essentially not a thread safe procedure. Even if the collection is synchronized, other threads can change the collection, which causes the enumerator to be excluded. To ensure thread safety during enumeration, you can block the collection during the entire enumeration or catch of exceptions as a result of changes made by other threads.

The following code example shows how to lock a collection using SyncRoot throughout an enumeration:

 Hashtable myCollection = new Hashtable(); lock(myCollection.SyncRoot) { foreach (object item in myCollection) { // Insert your code here. } } 
+8
source

Why is the second input outside the lock?

+2
source

You need to block while you delete, and also while you figure out what to delete. Move it

 foreach (ulong key in keysToRemove) sessionsTimeoutData.Remove(key); 

In a locked section.

+1
source

All Articles