-8

Here is the code that I was trying on my workstation.

class Program
{
    public static volatile bool status = true;

    public static void Main()
    {
        Thread FirstStart = new Thread(threadrun);
        FirstStart.Start();

        Thread.Sleep(200);

        Thread thirdstart = new Thread(threadrun2);
        thirdstart.Start();

        Console.ReadLine();

    }

    static void threadrun()
    {
        while (status)
        {
            Console.WriteLine("Waiting..");
        }
    }

    static void threadrun2()
    {
        status = false;
        Console.WriteLine("the bool value is now made FALSE");
    }
}

As you can see I have fired three threads in Main. Then using breakpoints I tracked the threads. My initial conception was all the three threads will be fired simultaneously, but my breakpoint flow showed that the thread-execution-flow followed one after other (and so was the output format i.e. Top to bottom execution of threads). Guys why is that happening ?

Additionally I tried to run the same program without using the volatile keyword in declaration, and I found no change in program execution. I doubt the volatile keyword is of no practical live use. Am I going wrong somewhere?

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
  • How would you expect this to happen when using the debugger? – JonH Dec 16 '11 at 19:32
  • 1
    Why do you believe that the threads would execute simultaneously? – Alan Dec 16 '11 at 19:33
  • Then why don't I get any difference in output while using and not using them ?? – deepak sahu Dec 16 '11 at 19:36
  • 1
    Of coure they don't run simultaneously. – Al Kepp Dec 16 '11 at 19:48
  • http://blogs.msdn.com/b/ericlippert/archive/2011/06/16/atomicity-volatility-and-immutability-are-different-part-three.aspx – Otiel Dec 16 '11 at 19:49
  • Thanks Otiel the Blog link was useful . – deepak sahu Dec 16 '11 at 20:01
  • You can't prove Volatile as useless on your Laptop/Desktop, You need Really Hardware stack and memory/CPU intensive operations. it not easy to feel pain of MultiThreading in Visual Studio/Laptop/Debugger. – dipak Dec 16 '11 at 20:25
  • 14
    You're worried about getting shot. You walk around a quiet suburban neighbourhood without wearing a bulletproof vest one day, and no one shoots at you, and you therefore conclude that bulletproof vests are unnecessary to stop bullets in a war zone? – Eric Lippert Dec 16 '11 at 22:12

4 Answers4

4

Your method of thinking is flawed.

The very nature of threading related issues is that they're non-deterministic. This means that what you have observed is potentially no indicator of what may happen in the future.

This is the very nature of why multithreaded programming is "hard." It often defies ad hoc testing, or even most unit testing. The only way to do it effectively is to understand your entire software and hardware stack, and diagram every possible occurrence through use of state machines.

In summary, threaded programming is not about what you've seen happen, it's about what might possibly happen, no matter how improbable.

Kennet Belenky
  • 2,755
  • 18
  • 20
3

Ok I will try to explain a very long story as short as possible:

Number 1: Trying to inspect the behavior of threads with the debugger is as useful as repeatedly running a multithreaded program and concluding that it works fine because out of 100 tests none failed: WRONG! Threads behave in a completely nondeterministic (some would say random) way and you need different methods to make sure such a program will run correctly.

Number 2: The use of volatile will become clear once you remove it and then run your program in Debug mode and then switch to Release mode. I think you will have a surprise... What happens in Release mode is that the compiler will optimize code (this includes reordering instructions and caching of values). Now, if your two threads run on different processor cores, then the core executing the thread that is checking for the value of status will cache its value instead of repeatedly checking for it. The other thread will set it but the first one will never see the change: deadlock! volatile prevents this kind of situation from occurring.

In a sense, volatile is a guard in case the code does not actually (and most likely will not) run as you think it will in a multithreaded scenario.

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • That's slightly misleading in several ways.. it's not so much release mode that's the problem, but running with no debugger attached. It doesn't matter whether it runs on different cores, time-sharing multithreading has exactly the same problem here, because it's a not a funny global ordering problem but a problem of "value is in a register instead of re-read from cache, so it never changes". – harold Dec 17 '11 at 13:24
  • 1
    @harold: I beg to differ on the debugger point. It's not a matter of having a debugger attached, it's a matter of compiling with or without optimizations. You can reproduce this problem even without using an IDE. – Tudor Dec 17 '11 at 13:42
  • Well that would be true for C, but the C# compiler doesn't optimize anyway, despite what the optimize flag may say. It's all the JIT compiler's fault. – harold Dec 17 '11 at 14:34
  • @harold: I just reproduced the problem compiling with csc /o. It doesn't happen without the flag. Try it yourself. – Tudor Dec 17 '11 at 14:38
  • Yes I already have, though not with this code, and there only debugger mattered. – harold Dec 17 '11 at 14:52
  • Which version of the CLR did you use by the way? – harold Dec 17 '11 at 14:59
  • I was using 2, I'll see what happens if I upgrade but I trust you're right – harold Dec 17 '11 at 15:11
  • Thank you every one guys... I conclude that Thread execution is completely NONDETERMINISTIC and solely depends on processor type and speed and code optimisation technique used. And Volatile Usability can be noted on a large scale computing machine and not in a small debugger. Thank you all again Happy coding. – deepak sahu Dec 19 '11 at 05:57
2

The fact that your simple code doesn't behave dirrefently with volatile doesn't mean anything. Your code is too simple and has nothing to do with volatile. You need to write very computation-intensive code to create a clearly visible memory race condition.

Also, volatile keyword may be useful on other platforms than x86/x64 with other memory models. (I mean like for example Itanium.)

Joe Duffy wrote interesting information about volatile on his blog. I strongly recommend to read it.

Al Kepp
  • 5,831
  • 2
  • 28
  • 48
  • Thank you every one guys... I conclude that Thread execution is completely NONDETERMINISTIC and solely depends on processor type and speed and code optimisation technique used. And Volatile Usability can be noted on a large scale computing machine and not in a small debugger. Thank you all again Happy coding. – deepak sahu Dec 19 '11 at 05:44
0

Then using breakpoints I tracked the threads. My initial conception was all the three threads will be fired simultaneously, but my breakpoint flow showed that the thread-execution-flow followed one after other (and so was the output format i.e. Top to bottom execution of threads). Guys why is that happening?

The debugger is temporarily suspending the threads to make it easier to debug.

I doubt the volatile keyword is of no practical live use. Am I going wrong somewhere?

The Console.WriteLine calls are very likely fixing masking the problem. They are most likely generating the necessary memory barrier for you implicitly. Here is a really simple snippet of code that demonstrates that there is, in fact, a problem when volatile is not used to declare the stop variable.

Compile the following code with the Release configuration and run it outside of the debugger.

class Program
{
    static bool stop = false;

    public static void Main(string[] args)
    {
        var t = new Thread(() =>
        {
            Console.WriteLine("thread begin");
            bool toggle = false;
            while (!stop)
            {
                toggle = !toggle;
            }
            Console.WriteLine("thread end");
        });
        t.Start();
        Thread.Sleep(1000);
        stop = true;
        Console.WriteLine("stop = true");
        Console.WriteLine("waiting...");
        t.Join();
    }
}
Brian Gideon
  • 47,849
  • 13
  • 107
  • 150