In the previous article, we have talked about race condition problem. So, in this article we will focus on one of the solutions (using critical section), the Monitor class, which is a mutual-exclusive thread synchronization construct.
The Monitor class is built on dotNET’s FCL (Framework Class Library) infrastructure. In general, it helps to achieve thread safety.
Thread Synchronization In Critical Section
Thread synchronization is used to prevent corruption when multiple threads access shared data at the same time; Because thread synchronization is all about timing.
The System.Threading.Monitor class basically helps us to avoid the race condition problem by protecting the critical section.
Let’s think that we have a code block that has a shared resource (variable or any state), and this resource is used in an executed context by multithread. So, in this case, we need to understand that the potential race condition problem exists for us. And also that code block is a critical section for us.
The Monitor class provides threads to enter one by one into the code block, i.e. it accepts the thread into the critical section and executes the block with a single thread, then releases the thread. This means that all the other threads must wait and halt the execution until the locked section is released. And this cycle will continue in the same way.
In other words, it helps us to synchronize the execution of threads between each other.
How to Use The Monitor Class?
Let’s simulate the race condition problem and then prevent to it with Monitor Class.
public class Program
{
// A basic field for the simulation of shared data
static int _sharedField = 0;
public static void Main()
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem((s) => IncrementTheValue());
}
Console.WriteLine("Main method is finished!");
Console.Read();
}
// The method variable will result inconsistent
static void IncrementTheValue()
{
if (_sharedField < 5)
{
Thread.Sleep(55); // just for simulation execution time!
_sharedField += 1;
Console.WriteLine($"Field: {_sharedField} | Thread: {Thread.CurrentThread.ManagedThreadId}");
}
else
{
Console.WriteLine($"Field count is complited {_sharedField} | Thread Id is {Thread.CurrentThread.ManagedThreadId}");
}
}
}
The code above shows that we have a main method that creates 10 threads, and they independently execute the IncrementTheValue method. The method steps are:
- if _sharedField is less than 5, then enter the code block
- Execution time simulation with Thread.Sleep(55) (fake operation)
- Increment the value of _sharedField
- Write into the console _sharedField value and current thread id
Well, if you run the code, you will see that the result of the _sharedField variable is above 5. So, we are caught in a race condition problem.
Hmm, let’s avoid this problem. First of all, we need to identify our critical section area, in our case, it is the IncrementTheValue method completely. Because the method uses IF and ELSE blocks. So, we need to guarantee that our IF block will not participate in the case of racing threads. Otherwise, the race condition problem will continue.
public class Program
{
// Should be reference type
// Should be private
static object _LOCKREF = new object();
// A basic field for the simulation of shared data
static int _sharedField = 0;
public static void Main()
{
for (int i = 0; i < 10; i++)
{
ThreadPool.QueueUserWorkItem((s) => IncrementTheValue());
}
Console.WriteLine("Main method is finished!");
Console.Read();
}
static void IncrementTheValue()
{
try
{
Monitor.Enter(_LOCKREF);
// Critical section start point
if (_sharedField < 5)
{
Thread.Sleep(55); // just for simulation execution time!
_sharedField += 1;
Console.WriteLine($"Field: {_sharedField} | Thread: {Thread.CurrentThread.ManagedThreadId}");
} // Critical section end point
else
{
Console.WriteLine($"Field count is complited {_sharedField} | Thread Id is {Thread.CurrentThread.ManagedThreadId}");
}
}
finally
{
// Ensure that the lock is released
Monitor.Exit(_LOCKREF);
}
}
}
Join the conversation! Your thoughts help the community grow.