Introduction
A race condition occurs when two or more threads are able to access shared data and they try to change it at the same time. Because the thread scheduling algorithm can swap between threads at any point, we cannot know the order at which the threads will attempt to access the shared data. Therefore, the result of the change in data is dependent on the thread scheduling algorithm, i.e. both threads are 'racing' to access/change the data. The reason for having more race conditions among threads than processes is that threads can share their memory while a process can not.
For example Let's say we have two threads, T1 and T2, where both are accessing a shared variable x. They first read data from the variable and then try to write data on the variable simultaneously. They would race to find which of these threads can write the value last to the variable. In this case, the last written value would be saved.
As another example, we can have these two threads, T1 and T2, trying opposite operations on this variable. Let thread T1 increase the value of the variable and the thread T2 decrease the value of the variable at the same point of time. What is the result? The value remains unchanged.
Race Conditions
Consider the example shown below. We have a variable result and there are 3 threads trying to write a unique value to it
using
System;
using
System.Threading;
namespace
RaceCondition
class Akshay
{
int result = 0;
void Work1() { result = 1; }
void Work2() { result = 2; }
void Work3() { result = 3; }
static void
Main(string[] args)
{
Akshay a =
new Akshay();
Thread worker1 =
new Thread(a.Work1);
Thread worker2 =
new Thread(a.Work2);
Thread worker3 =
new Thread(a.Work3);
worker1.Start();
worker2.Start();
worker3.Start();
Console.WriteLine(a.result);
Console.Read();
}
}
}
output :
Looking at the code it might seem that the result
at the end will be 3. But if you try executing the code you will find out that
it is not always true. The result might be 1, 2 ,3 or even 0 as



The reason for this is that the operating system decides which thread gets executed first. And the order in which we start the threads is not all that important. So thread that is given least priority by the operating system gets executed last.
The Solution
C# provide number of ways to avoid race conditions depending on the type of application you are writing. But the most common method that works in any condition is using Wait Handles and Signaling. In the above example we will try to ensure that the first thread is the last one that writes value to result variable.
The process is quite simple let all threads are started at the same time. But the first thread is forced to wait till second and third thread signal that they are done.
Similarly the main thread is forced to wait till the first thread completes. This ensures that the Console.WriteLine() is not called before the other threads finish their work.
using
System;
using
System.Threading;
namespace
RaceCondition
{
class
Akshay
{
int result = 0;
AutoResetEvent event1 =
new AutoResetEvent(false);
AutoResetEvent event2 =
new AutoResetEvent(false);
AutoResetEvent event3 =
new AutoResetEvent(false);
void Work1()
{
WaitHandle.WaitAll(new
WaitHandle[] { event2, event3 });
result = 1;
event1.Set();
}
void Work2() { result = 2; event2.Set(); }
void
Work3() { result = 3; event3.Set(); }
static void Main(string[]
args)
{
Akshay a = new
Akshay();
Thread worker1 = new
Thread(a.Work1);
Thread worker2 = new
Thread(a.Work2);
Thread worker3 = new
Thread(a.Work3);
WaitHandle[] waitHandles =
new WaitHandle[]
{ a.event2, a.event3 };
worker1.Start();
worker2.Start();
worker3.Start();
WaitHandle.WaitAny(new
WaitHandle[] { a.event1 });
Console.WriteLine(a.result);
Console.Read();
}
}
}
Output

To implement this we need to declare AutoResetEvent for each thread (except the main one). Whenever a thread finishes execution it signals its respective AutoResetEvent by calling Set(). The first thread waits for both second and third thread to signal and then starts execution.
Similarly the main thread waits for the first thread to signal before it prints the value. This method will always ensure that the final result is 1.

Sujit PrabhakaranPosted Mar 28, 2024, 12:14 AM
Even now, I am challenged by this question in an interview yesterday. Dear Akshay, can you share with me more details on WaitHandles. If possible share with me the online resource or ebook where you learnt this concept. I would like to read more as I am still not getting a clear picture.
Mahdi bycheckPosted Jun 26, 2021, 4:46 AM
Thanks for this usfuel text
Navnath KumbharPosted Apr 24, 2018, 12:05 AM
Good Explanation! Thank you.
Nagi APosted Apr 7, 2017, 4:03 AM
Good Explanation. Thank you Akshay
Jeetesh SrivastavaPosted Mar 21, 2012, 7:51 AM
thanks as it was very useful