Data Races & Synchronization in .NET 4.5







Data Races

A data race—or race condition—occurs when data is accessed concurrently from multiple threads. Specifically, it happens when one or more threads are writing a piece of data while one or more threads are also reading that piece of data. This problem arises because Windows programs (in C++ and the Microsoft .NET Framework alike) are fundamentally based on the concept of shared memory, where all threads in a process may access data residing in the same virtual address space.

In general, a race condition is when non-deterministic behavior results from threads accessing shared data or resources without following suitable synchronization protocols or locks for serializing threads and to ensure single-threaded access. Accessing shared data from multiple threads concurrently requires that either that shared state be immutable or that the application utilize synchronization to ensure the consistency of the data.


The following code ensures that the work inside the critical region is executed by at most one thread at a time.

Leaking Locks

The external influences may cause exceptions to occur on a block of code even if that exception is not explicitly stated in the code. This problem is called “Asynchronous Exceptions“. For example, a thread abort may be injected into a thread between any two instructions, though not within a finally block except in extreme conditions. If such an abort occurred after the call to Monitor.Enter but prior to entering the try block, the monitor would never be exited, and the lock would be “leaked.” To help prevent against this, the just-in-time (JIT) compiler ensures that, as long as the call to Monitor.Enter is the instruction immediately before the try block, no asynchronous exception will be able to sneak in between the two. Unfortunately, it is not always the case that these instructions are immediate neighbors. it is often the case that developers want to enter a lock conditionally, such as with a timeout, and in such cases there are typically branching instructions between the call and entering the try block.

Reliable Locking

To address this, in the .NET Framework 4 new overloads of Monitor.Enter and Monitor.TryEnter have been added, supporting a new pattern of reliable lock acquisition and release:

public static void Enter(object obj, ref bool lockTaken);

This overload guarantees that the lockTaken parameter is initialized by the time Enter returns, even in the face of asynchronous exceptions. This leads to the following new, reliable pattern for entering a lock:

Copyright © All Rights Reserved - C# Learners