Thread-Safety and .NET Framework Types
Thread-safety means, code has no indeterminacy in the case of any multithreading scenario.
Thread-safety is achieved primarily with locking, and by reducing the possibilities for interaction between threads.
A method which is thread-safe in any scenario is called Reentrant.
Here we’ll study two important points with respect to .NET framework types.
- Within the .NET framework – Nearly all of its Non-Primitive Types are not Thread Safe when instantiated.
- Common pattern throughtout the .NET framework - Static Members are Thread-Safe, while instance members are not (above statement).
A). Nearly all of its Non-Primitive Types are not Thread Safe
However, within the .NET framework – nearly all of its Non-Primitive Types are not thread safe when instantiated, for the following reasons:
- Thread-safety can entail a Performance Cost (payable, in part, whether or not the type is actually used by multiple threads)
- The development burden in full thread-safety can be significant, particularly if a type has many fields (each field is a potential for interaction in an arbitrarily multi-threaded context)
Thread-safety is hence usually implemented just where it needs to be, in order to handle a specific multithreading scenario.The way to provide Thread-Safety – Locking
Locking can be used to convert thread-unsafe code into thread-safe code.i.e if all access to any given object is protected via a lock, all of .NET non-primitive types which are not thread safe when instantiated,
can be used in multi-threaded code.
Note - Here in this example, we're locking on the list object itself, which is fine in this simple scenario. If we had two interrelated lists, however, we would need to lock upon a common object – perhaps a separate field, if neither list presented itself as the obvious candidate.
| || |
static List<string> list = new List<string>();
static void AddItems()
for (int i = 0; i < 100; i++)
list.Add("Item " + list.Count);
items = list.ToArray();
foreach (string s in items)
Here notice in the Grayed Code - Enumerating .NET collections is also Thread-Unsafe in the sense that an exception is thrown if another thread alters the list during enumeration. Thus rather than locking for the duration of enumeration, in this example, we first copy the items to an array. This avoids holding the lock excessively if what we're doing during enumeration is potentially time-consuming
Here notice, we have been using Locks At Multiple Places. Here's an interesting supposition: imagine if the List class was, indeed, Thread-Safe in itself. Would these multiple locks required?
To illustrate, let's say we wanted to add an item to our hypothetical thread-safe list, as follows:
| || |
if (!myList.Contains (newItem))
Whether or not the list was initself thread-safe, this statement is certainly not!
The whole if statement would have to be wrapped in a lock – to prevent preemption in between testing for containership and adding the new item.
This same lock would then need to be used everywhere we modified that list. For instance, the following statement would also need to be wrapped – in the identical lock: to ensure it did not preempt the former statement.
Thumb Rule - In other words, we would have to lock our hypothetical thread-safe list almost exactly as with our Thread-Unsafe collection classes.
Built-in thread safety, then, can actually be a Waste Of Time!
B). Static Members are Thread-Safe, while instance members are not
Above we mentioned - Built-in thread safety, can actually be a Waste Of Time!
One could argue this point when writing custom components – why to code built in thread-safety, when it can easily end up being redundant – by requiring multiple locks (as shown in above code).
The answer to this question – If there is no built in Thread Safety, then it becomes responsibility of developers to always to wrap an object around a custom lock. But this seems quite impractical if the object is publicly/widely scoped - The worst scenario crops up with Static Members In A Public Type.
For instance, imagine the static property on the DateTime struct, DateTime.Now, was not thread-safe, and that two concurrent calls could result in garbled output or an exception. The only way to remedy this with external locking might be to lock the type itself – lock(typeof(DateTime)) – around calls to DateTime.Now – which would work only if all developers agreed to do this. And this is less possible, given that locking a type is considered by many, a Bad Thing!
For this reason, static members on the DateTime struct are guaranteed to be thread-safe. This is a common pattern throughout the .NET framework – static members are thread-safe, while instance
members are not.
Hope this helps.
Thanks & Regards,
Arun Manglick || Senior Tech Lead