I wanted to understand on when exactly I need to declare a variable as volatile. For that I wrote a small program and was expecting it to go into infinite loop because of missing volatility of a condition variable. It did not went into infinite loop and worked fine without volatile keyword.
Two questions:
-
What should I change in the below code listing – so that it absolutely requires use of volatile?
-
Is C# compiler smart enough to treat a variable as volatile – if it sees that a variable is being accessed from a different thread?
The above triggered more questions to me 🙂
a. Is volatile just a hint?
b. When should I declare a variable as volatile in context of multithreading?
c. Should all member variables be declared volatile for a thread safe class? Is that overkill?
Code Listing (Volatility and not thread safety is the focus):
class Program
{
static void Main(string[] args)
{
VolatileDemo demo = new VolatileDemo();
demo.Start();
Console.WriteLine("Completed");
Console.Read();
}
}
public class VolatileDemo
{
public VolatileDemo()
{
}
public void Start()
{
var thread = new Thread(() =>
{
Thread.Sleep(5000);
stop = true;
});
thread.Start();
while (stop == false)
Console.WriteLine("Waiting For Stop Event");
}
private bool stop = false;
}
Thanks.
Try to rewrite it like this:
And make sure you are running in Release mode and not Debug mode. In Release mode optimizations are applied which actually cause the code to fail in the absence of
volatile.Edit: A bit about
volatile:We all know that there are two distinct entities involved in a program lifecycle that can apply optimizations in the form of variable caching and/or instruction reordering: the compiler and the CPU.
This means that there may be even a large difference between how you wrote your code and how it actually gets executed, as instructions may be reordered with respect to eachother, or reads may be cached in what the compiler perceives as being an “improvement in speed”.
Most of the times this is good, but sometimes (especially in the multithreading context) it can cause trouble as seen in this example. To allow the programmer to manually prevent such optimizations, memory fences were introduced, which are special instructions whose role is to prevent both reordering of instructions (just reads, just writes or both) with respect to the fence itself and also force the invalidation of values in CPU caches, such that they need to be re-read every time (which is what we want in the scenario above).
Although you can specify a full fence affecting all variables through
Thread.MemoryBarrier(), it’s almost always an overkill if you need only one variable to be affected. Thus, for a single variable to be always up-to-date across threads, you can usevolatileto introduce read/write fences for that variable only.