TPL Collections
new concurrent collections lock-free?
All of the collections in the new System.Collections.Concurrent
namespace employ lock-free techniques to some extent in order to achieve general performance benefits, but traditional locks are used in some cases.
It’s worth noting that purely relying on lock-free techniques is sometimes not the most efficient solution. When we say “lock-free,” we mean that locks (in .NET, traditional mutual exclusion locks are available via the System.Threading.Monitor
class, typically via the C# lock
keyword or the Visual Basic SyncLock
keyword) have been avoided by using memory barriers and compare-and-swap CPU instructions (in .NET, “CAS” operations are available via the System.Threading.Interlocked
class).
ConcurrentQueue<T>
and ConcurrentStack<T>
are completely lock-free in this way. They will never take a lock, but they may end up spinning and retrying an operation when faced with contention (when the CAS operations fail).
ConcurrentBag<T>
employs a multitude of mechanisms to minimize the need for synchronization. For example, it maintains a local queue for each thread that accesses it, and under some conditions, a thread is able to access its local queue in a lock-free manner with little or no contention. Therefore, while ConcurrentBag<T>
sometimes requires locking, it is a very efficient collection for certain concurrent scenarios (e.g. many threads both producing and consuming at the same rate).
ConcurrentDictionary<TKey,TValue>
uses fine-grained locking when adding to or updating data in the dictionary, but it is entirely lock-free for read operations. In this way, it’s optimized for scenarios where reading from the dictionary is the most frequent operation.