Is DictionaryKey, value is thread safe for read, write and remove operation
No. A dictionary is not thread safe.
In fact, it's documentation specifically states that -
A Dictionary<TKey,TValue> can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
For thread-safe alternatives, see the ConcurrentDictionary<TKey,TValue> class or ImmutableDictionary<TKey,TValue> class.
Is accessing the DictionaryTKey, TValue Keys property thread safe?
No, Dictionary
is not thread-safe. If you need thread safety and you're using .NET 4 or higher, you should use a ConcurrentDictionary
.
From MSDN:
A Dictionary can support multiple readers concurrently,
as long as the collection is not modified. Even so, enumerating
through a collection is intrinsically not a thread-safe procedure. In
the rare case where an enumeration contends with write accesses, the
collection must be locked during the entire enumeration. To allow the
collection to be accessed by multiple threads for reading and writing,
you must implement your own synchronization.
Dictionary Update Thread-Safety
No, Dictionary<TKey, TValue>
class is not thread-safe for modification, as can be seen in the documentation:
A
Dictionary<TKey, TValue>
can support multiple readers concurrently, as long as the collection is not modified. Even so, enumerating through a collection is intrinsically not a thread-safe procedure. In the rare case where an enumeration contends with write accesses, the collection must be locked during the entire enumeration. To allow the collection to be accessed by multiple threads for reading and writing, you must implement your own synchronization.
In your case, if some threads will finish the LongTaskToGenerateString
almost simultaneously their dictionary updates will interference.
You might either use the SyncRoot
property to synchronize the access manually, or just take the ConcurrentDictionary<TKey, TValue>
class, as suggested by asawyer in the comments.
This implementation suggests that if you're only updating the values for existing keys, it should be OK (looking also at this) - the inelegant effect could be inaccurate value of the version
property. It is used to guard against modifying the collection while it's being enumerated, so it doesn't really matter at which value it will end up. Don't know about any guarantees about this though.
c# when I use only TryGetValue on dictionary.. it's thread-safe?
Yes, it's thread safe if you only read it/ use TryGetValue
:
Documentation:
A
Dictionary<TKey, TValue>
can support multiple readers concurrently,
as long as the collection is not modified. Even so, enumerating
through a collection is intrinsically not a thread-safe procedure. In
the rare case where an enumeration contends with write accesses, the
collection must be locked during the entire enumeration. To allow the
collection to be accessed by multiple threads for reading and writing,
you must implement your own synchronization.
Are nested Dictionaries thread safe from parent ConcurrentDictionary?
Of course not, they're different dictionaries with different access rules.
Your example however is fine, because you're accessing different dictionaries from different threads. If you were to do this instead:
outerDict[30]["a"] = "b"; // in thread 1
outerDict[30]["g"] = "h"; // in thread 2
You'd quickly run into issues.
Thread safety of concurrent writes with discrete keys in Dictionary, indexer
First of all, I want to note that I got similar situation in recent project, where we have a dictionary with keys a DateTimes
(unique), dealing with it in parallel, and after initializing it we sometimes got issues with KeyNotFoundException
, but we didn't preallocate the memory like you. May be the issues are solved with it? Let's talk about code you've linked.
My teacher for multi-threading programming always tells us the very same thing each time we got a question regarding the concurrency:
What if billions of threads will be here right in this moment?
So let's try to see is there a possible problem in the Dictionary
.
dictionary[key] = new object()
leads us to
set {
Insert(key, value, false);
}
Insert
is the main adding method, being called from many places in the Dictionary
class. As you state the objects are unique, I assume that there will be no hash collisions there, and no overriding the values in first loop of methods, so let's see the rest of the code:
int index;
if (freeCount > 0) {
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
}
As you initialized the dictionary with capacity 2500
, else
clause seems not to called at all during such situation, so let's examine if
part:
1. if (freeCount > 0) {
2. // atomic assign
3. index = freeList;
4. // some calculation and atomic assign
5. freeList = entries[index].next;
6. // not threadsafe operation
7. freeCount--;
8. }
Seems like we have multiple multi-threading issues here:
freeList
andfreeCount
fields aren'tvolatile
, so read/write to it can be error-prone.What if billions of threads will be here right in this moment?: ©
3. index = freeList;
Billion of threads will get the same index, as there is no synchronization between read and write for thefreeList
field! And after that they'll override the value for each other with a race condition:entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
version++;decrement
operation isn't thread-safe (great interesting answer from @EricLippert), so we'll have potentially corrupted state for the dictionary.- What if billions of threads will be here right in this moment?: ©
5. freeList = entries[index].next;
Some thread gets thefreeList
value intoindex
variable, say we have5
there (the entries contains the linked list inside them, with head equal to-1
) and became inactive before overriding thefreeList
. Billion of threads advances the linked list position after position, and now our first one became active. He happily overrides thefreeList
with obsolete data, creating a ghosts in linked list, and data there can be overridden.
So there is a lot of issues can happen during your code execution, and personally I don't recommend you to use Dictionary
class in such circumstances.
Which members of .NET's ConcurrentDictionary are thread-safe?
Notice the section of the documentation that covers explicit interface implementations. E.g. the class implements IDictionary.Add
. This method is not a public or protected member of the class, but may be accessed via the IDictionary
interface. It is these such members that are not being guaranteed to be thread safe.
Thread safety of a DictionaryTKey, TValue with multiple concurrent readers and no writers
For your future reference, the documentation is here:
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx
It says:
A Dictionary
can support multiple readers
concurrently, as long as the
collection is not modified. Even so,
enumerating through a collection is
intrinsically not a thread-safe
procedure. In the rare case where an
enumeration contends with write
accesses, the collection must be
locked during the entire enumeration.
To allow the collection to be accessed
by multiple threads for reading and
writing, you must implement your own
synchronization.
ConcurrentDictionaryTKey,TValue vs DictionaryTKey,TValue
Without knowing more about what you're doing within the lock, then it's impossible to say.
For instance, if all of your dictionary access looks like this:
lock(lockObject)
{
foo = dict[key];
}
... // elsewhere
lock(lockObject)
{
dict[key] = foo;
}
Then you'll be fine switching it out (though you likely won't see any difference in performance, so if it ain't broke, don't fix it). However, if you're doing anything fancy within the lock block where you interact with the dictionary, then you'll have to make sure that the dictionary provides a single function that can accomplish what you're doing within the lock block, otherwise you'll end up with code that is functionally different from what you had before. The biggest thing to remember is that the dictionary only guarantees that concurrent calls to the dictionary are executed in a serial fashion; it can't handle cases where you have a single action in your code that interacts with the dictionary multiple times. Cases like that, when not accounted for by the ConcurrentDictionary
, require your own concurrency control.
Thankfully, the ConcurrentDictionary
provides some helper functions for more common multi-step operations like AddOrUpdate
or GetOrAdd
, but they can't cover every circumstance. If you find yourself having to work to shoehorn your logic into these functions, it may be better to handle your own concurrency.
Related Topics
Get Ipv4 Addresses from Dns.Gethostentry()
How to Get Around Lack of Covariance with Ireadonlydictionary
Add Vertical Scroll Bar to Panel
Using Custom Colored Cursors in a C# Windows Application
Should I Transform Entity (Persistent) Objects to Dto Objects
Cross-Thread Winforms Control Editing
Save Modified Wordprocessingdocument to New File
Task.Factory.Startnew with Async Lambda and Task.Waitall
How to Prevent a Single Object Property from Being Converted to a Datetime When It Is a String