Volatile keyword in C#
Volatile keyword is so important in the .Net Framework. In order to understand what is Volatile, we must have answer of "What is threading & what is the use of threading in C#"?
Here we have also an important question: "Why we need this keyword and what happen if we don't use this keyword in our application?"
Look at the following code and image:
- public class myThreadProgram
- bool checkVariable= true;
- static void Main(string args)
- myThreadProgram myCustomProgram = new myThreadProgram();
- Thread childThread = new Thread(ChildThread);
- childThread .Start(myCustomProgram );
- myCustomProgram .checkVariable= false;
- Console.WriteLine("Change checkVariable false");
- private static void ChildThread(myThreadProgram objProgram)
- Console.WriteLine("Child Thread Loop Starting...");
- while (objProgram.checkVariable)
- Console.WriteLine("Child Thread Loop Stopping...");
In the above code we can see there is a variable named "checkVariable". In Main method we have objects of Program and Thread class named "myCustomProgram & childThread". Here we are passing the method name with Constructor of Thread class. Now move to the next line where we start our thread with Program object. Then after we used sleep method of Thread class and passing some values. In next line we access the variable "checkVariable" through myCustomProgram and set value "false". Here myCustomProgram is belonging to our current running program.
When we executed the above code, It will start two separate thread. First thread is default main program thread and the second one is custom thread which we started manually in our code.
Now let's take a look what are happening internally when we execute the above code?
Here when we execute the above code all threads create their own local memories and copy all main memory variables and methods and then after all threads communicate their own local variables and methods. Whenever they update any values then after Sync that into main memory location variables.
Here again take a look the scenario, when we update checkVariable is false in our main method, thread update their local variable value and Sync that into main memory location. so now checkVariable is false but the problem is child thread still running because of checkVariable in their local memory are not updated.
Look at the following image:
In the above image we can understand when we work with multiple threads, all threads have their own local memory and any update in local memory sync with main memory but that will be not reflected in other threads local memory. So when multiple thread working on common variables it will create problem. We may be wondering…
What is the solution of this problem?
Now Volatile keyword comes into frame in .Net Framework to solve this problem. When we declare any variable with Volatile keyword thread access this variable from main memory location, means they don't create copy of variable into their local memory. I am very glad that C# has ensured that any volatile read or write is also an atomic read or write. It does little bit slow down our program but this address this confusion.
"The Volatile keyword instructs the compiler to generate an acquire-fence on every read from that field, and a release-fence on every write to that field. An acquire-fence prevents other reads/writes from being moved before the fence; a release-fence prevents other reads/writes from being moved after the fence. These “half-fences” are faster than full fences because they give the runtime and hardware more scope for optimization."
Volatile keyword means "tell the processors to do whatever it is they need to do to ensure that I am reading the latest value, even if that means halting other processors and making them synchronize main memory with their caches".
Now, let's take a look at the following image:
In the above image we can see the scope of Volatile keyword but in the all scenario it is not truth. Now, look at the following code:
- class ConfusionWithVolatile
- volatile int x, y;
- void Test1() // Executed on one thread
- x = 1; // Volatile write (release-fence)
- int a = y; // Volatile read (acquire-fence)
- void Test2() // Executed on another thread
- y = 1; // Volatile write (release-fence)
- int b = x; // Volatile read (acquire-fence)
After go through the above code we can notice that applying volatile doesn’t prevent a write followed by a read from being swapped, and this can create brainteasers. Joe Duffy illustrates the problem well with the following example: if Test1() and Test2() method run simultaneously on different threads, it’s possible for a and b to both end up with a value of 0 (despite the use of Volatile on both x and y
This presents a strong case for avoiding Volatile: even if we understand the subtlety in this example, will other developers working on our code also understand it? A full fence between each of the two assignments in Test1() and Test2() (or a lock) solves the problem.
The Volatile keyword is not supported with pass-by-reference arguments or captured local variables: in these cases you must use the VolatileRead and VolatileWrite methods.
Frankly, I discourage you from ever making a Volatile field. Volatile fields are a sign that we are doing something downright crazy: we're attempting to read and write the same value on two different threads without putting a lock in place. Locks guarantee that memory read or modified inside the lock is observed to be consistent, locks guarantee that only one thread accesses a given hunk of memory at a time, and so on. The number of situations in which a lock is too slow is very small, and the probability that we are going to get the code wrong because we don't understand the exact memory model is very large. We don't attempt to write any low-lock code except for the most trivial usages of Interlocked operations.
Here, I leave the usage of "Volatile" to real experts.