Is it possible for a Dictionary in .Net to cause dead lock when reading and writing to it in parallel?
So your code is executing Dictionary.FindEntry
. It's not a deadlock - a deadlock happens when two threads block in a way which makes them wait for one another to release a resource, but in your case you're getting two seemingly infinite loops. The threads aren't locked.
Let's take a look at this method in the reference source:
private int FindEntry(TKey key) {
if( key == null) {
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
}
if (buckets != null) {
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next) {
if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;
}
}
return -1;
}
Take a look at the for
loop. The increment part is i = entries[i].next
, and guess what: entries
is a field which is updated in the Resize
method. next
is a field of the inner Entry
struct:
public int next; // Index of next entry, -1 if last
If your code can't exit the FindEntry
method, the most probable cause would be you've managed to mess the entries in such a way that they produce an infinite sequence when you're following the indexes pointed by the next
field.
As for the Insert
method, it has a very similar for
loop:
for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next)
As the Dictionary
class is documented to be non-threadsafe, you're in the realm of undefined behavior anyway.
Using a ConcurrentDictionary
or a locking pattern such as a ReaderWriterLockSlim
(Dictionary
is thread-safe for concurrent reads only) or a plain old lock
nicely solves the problem.
Looks like a race condition (not a deadlock) - which, as you comment, causes the messed up internal state.
The dictionary is not thread safe so concurrent reads and writes to the same container from seperate threads (even if there are as few as one of each) is not safe.
Once the race condition is hit, it becomes undefined what will happen; in this case what appears to be an infinite loop of some sort.
In general, once write access is required, some form of synchronization is required.