4

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:

  1. What should I change in the below code listing - so that it absolutely requires use of volatile?

  2. 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.

blueshift
  • 6,742
  • 2
  • 39
  • 63
Anand Patel
  • 6,031
  • 11
  • 48
  • 67
  • 1
    I have never seen volatile used correctly - I'm not even completely sure what the correct use of volatile is. There are some subtleties I think that almost everyone misses. One of your problems though (I think) is that the thread doing the setting ends. When the thread completes I would think it needs to flush out any values it had stored in registers since it's no longer doing anything. – Russell Troywest Jan 03 '12 at 09:13
  • Did it even write to the console `Waiting For Stop Event`? – ediblecode Jan 03 '12 at 09:16
  • 1
    http://stackoverflow.com/questions/133270/how-to-illustrate-usage-of-volatile-keyword-in-c-sharp – jeroenh Jan 03 '12 at 09:18
  • @RussellTroywest: I understand that the thread doing the setting will eventually (may be at the same time or when thread ends) update the value in memory. I was expecting the main thread to always read from register (at least, in absence of volatile) rather than reading it from memory every time the condition gets evaluated. – Anand Patel Jan 03 '12 at 09:18
  • @user1016253: Yes, it did write the 'Waiting For Stop Event' multiple times to the console. – Anand Patel Jan 03 '12 at 09:19
  • Are you sure you are running in Release mode and not Debug mode? – Tudor Jan 03 '12 at 11:11
  • I personally avoid using the volatile keyword and use traditional locking. Albahari indicates that its use can be dangerous as it does not protect a write followed by a read on volatile variables from being swapped. See http://www.albahari.com/threading/part4.aspx#_Memory_Barriers_and_Volatility – Jeb Jan 03 '12 at 11:43
  • @Tudor: Tried with both, debug and release mode. – Anand Patel Jan 03 '12 at 12:24

5 Answers5

4

Firstly, Joe Duffy says "volatile is evil" - that's good enough for me.

If you do want to think about volatile, you must think in terms of memory fences and optimisations - by the compiler, jitter and CPU.

On x86, writes are release fences, which means your background thread will flush the true value to memory.

So, what you are looking for is a caching of the false value in your loop predicate. The complier or jitter may optimise the predicate and only evaluate it once, but I guess it doesn't do that for a read of a class field. The CPU will not cache the false value because you are calling Console.WriteLine which includes a fence.

This code requires volatile and will never terminate without a Volatile.Read:

static void Run()
{
    bool stop = false;

    Task.Factory.StartNew( () => { Thread.Sleep( 1000 ); stop = true; } );

    while ( !stop ) ;
}
Nick Butler
  • 24,045
  • 4
  • 49
  • 70
  • @Nichloas Butler: I tried your code, it is getting terminated properly without causing infinite loop. – Anand Patel Jan 03 '12 at 12:40
  • Did you run it with optimisations enabled ( Release build ) and without a debugger attached? – Nick Butler Jan 03 '12 at 12:47
  • My mistake. You r right. It goes in infinite loop with Release mode without debugger attached. Your solution creates anonymous class containing a method and stop field in it. Conceptually it is no different than my code. I need to thoroughly understand your response. Many thanks. – Anand Patel Jan 03 '12 at 15:48
3

I am not an expert in C# concurrency, but AFAIK your expectation is incorrect. Modifying a non-volatile variable from a different thread does not mean that the change will never become visible to other threads. Only that there is no guarantee when (and if) it happens. In your case it did happen (how many times did you run the program btw?), possibly due to the finishing thread flushing its changes as per @Russell's comment. But in a real life setup - involving more complex program flow, more variables, more threads - the update may happen later than 5 seconds, or - maybe once in a thousand cases - may not happen at all.

So running your program once - or even a million times - while not observing any problems only provides statistical, not absolute proof. "Absence of evidence is not evidence of absence".

Péter Török
  • 114,404
  • 31
  • 268
  • 329
2

Try to rewrite it like this:

    public void Start()
    {
        var thread = new Thread(() =>
        {
            Thread.Sleep(5000);
            stop = true;
        });

        thread.Start();

        bool unused = false;
        while (stop == false)
            unused = !unused; // fake work to prevent optimization
    }

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 use volatile to introduce read/write fences for that variable only.

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • I tried what u have sugggested, it still does not work. Tried with debug and release mode. – Anand Patel Jan 03 '12 at 12:30
  • @Anand Patel: Very strange because I just tested this and it works for me. Can you please check in your project properties that you have "optimize code" enabled? Which version of .NET are your using? – Tudor Jan 03 '12 at 14:19
  • My mistake. I now ran in release mode without debugger attached and it went into infinite loop. Many thanks. – Anand Patel Jan 03 '12 at 15:55
  • +1. Need to go through all the responses thoroughly once again to select the best answer ;). You might want to answer other sub-questions that are part of the question. – Anand Patel Jan 03 '12 at 16:05
  • Ah sorry, I didn't see the other, more theoretical questions about volatile. I'll give some insight of my own. – Tudor Jan 03 '12 at 16:08
  • Tudor: Marking your response as answer as it is close to my expectation. Equally appreciate the responses given by Peter Torok, Nicholas Butler and others. – Anand Patel Jan 05 '12 at 09:54
1

volatile keyword is a message to a compiler not to make single-thread optimizations on this variable. It means that this variable may be modified by multi threads. This makes the variable value the most 'fresh' while reading.

The piece of code you've pasted here is a good example to use volatile keyword. It's not a surprise that this code works without 'volatile' keyword. However it may behave more unpredictible when more threads are running and you perform more sophisticated actions on the flag value.

You declare volatile only on those variables which can be modified by several threads. I don't know exactly how it is in C#, but I assume you can't use volatile on those variables which are modified by read-write actions (such as incrementation). Volatile doesn't use locks while changing the value. So setting the flag on volatile (like above) is OK, incrementing the variable is not OK - you should use synchronization/locking mechanism then.

disorder
  • 291
  • 1
  • 7
  • +1. Very clear explanation. Does it mean that all the fields in my thread safe class needs to be volatile? – Anand Patel Jan 03 '12 at 16:35
  • Not necessarily. If you have a field which is never shared by many threads (it's internal for every thread) you may live it without volatile keyword. – disorder Jan 03 '12 at 20:25
1

When the background thread assigns true to the member variable there is a release fence and the value is written to memory and the other processor's cache is updated or flushed of that address.

The function call to Console.WriteLine is a full memory fence and its semantics of possibly doing anything (short of compiler optimisations) would require that stop not be cached.

However if you remove the call to Console.WriteLine, I find that the function is still halting.

I believe that the compiler in the absence of optimisations the compiler does not cache anything calculated from global memory. The volatile keyword is then an instruction not to even think of caching any expression involving the variable to the compiler / JIT.

This code still halts (at least for me, I am using Mono):

public void Start()
{
    stop = false;

    var thread = new Thread(() =>
    {
        while(true)
        {
            Thread.Sleep(50);
            stop = !stop;
        }
    });

    thread.Start();

    while ( !(stop ^ stop) );
}

This shows that it's not the while statement preventing caching, because this shows the variable not being cached even within the same expression statement.

This optimisation look sensitive to the memory model, which is platform dependent meaning this would be done in the JIT compiler; which wouldn't have time (or intelligence) to /see/ the usage of the variable in the other thread and prevent caching for that reason.

Perhaps Microsoft doesn't believe programmers capable of knowing when to use volatile and decided to strip them of the responsibility, and then Mono followed suit.

eyesathousand
  • 587
  • 2
  • 10