Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this program does not go into infinite loop in absence of volatility of a boolean condition variable?

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.

like image 772
Anand Patel Avatar asked Dec 22 '22 04:12

Anand Patel


2 Answers

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 ) ;
}
like image 58
Nick Butler Avatar answered Dec 24 '22 02:12

Nick Butler


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

like image 30
Péter Török Avatar answered Dec 24 '22 02:12

Péter Török