2

I have the following code:

 public static void Main(string[] args)
    {
        bool isComplete = false;

        var t = new Thread(() =>
        {
            int i = 0;
            while (!isComplete) i += 0;
        });

        t.Start();

        Thread.Sleep(500);
        isComplete = true;
        t.Join();
        Console.WriteLine("complete!");

    }

The program will hung in release mode and will give an output in debug mode ("complete!").

What is the reason for that?

Thanks.

2
  • "Why this unsafe multithreading code behaves erratically" is not really useful question... While it is entertaining coding puzzle there is zero practical value in this... Commented Jan 20, 2018 at 19:48
  • @AlexeiLevenkov I wouldn't be surprised to see such code in real life in a production environment and people wandering why it's wrong and not working like on their development machine and then deciding to run debug builds in production. All just because of some optimizations they aren't aware of. Commented Jan 20, 2018 at 20:52

1 Answer 1

10

The value of the variable isComplete is most likely being loaded into a register in your release build to avoid the round-trip to read value from memory in the while loop.

Therefore, the loop will not "detect" when the value of isComplete is changed to true.

You need to indicate to the compiler that this variable is volatile: essentially telling the system to not make assumptions based on code executing in the current thread whether or not this memory changes (i.e., some other thread or process might change it).

See this SA answer for some details:

How do I Understand Read Memory Barriers and Volatile - StackOverflow

If you want to dig even deeper into memory and concurrency, then here is an excellent article by Fabian Giesen about cache coherency in multi-core systems:

Cache coherency primer

Atomic operations and contention

Here are good reasons why you should just use a lock unless you know what you are doing:

Volatile keyword usage vs Lock - StackOverflow

Here is MSDN doc on Volatile.Read method:

Volatile.Read Method()

Note that without some more detailed explanation (see above SA thread, or google) of the terms, the description in the MSDN documentation can be hard to translate into what is actually going on.

Here is the MSDN doc on volatile keyword:

volatile (C# Reference)

In C# this keyword will use a half-fence (as far as I know); you will also find this keyword in C for example, but there it only affects compiler optimizations (does not insert a memory barrier) as it was originally intended for reading memory-mapped I/O.

Example:

        bool isComplete = false;

        var t = new Thread(() =>
        {
            int i = 0;
            while (!Volatile.Read(ref isComplete)) i += 0;
        });

        t.Start();

        Thread.Sleep(500);
        isComplete = true;
        t.Join();
        Console.WriteLine("complete!");
Sign up to request clarification or add additional context in comments.

10 Comments

Side note: volatile is wrong solution to write correct multithreading code, but it is fine for this fake example.
Indeed volatile keyword is not a magical solution for solving concurrency problems. Actual problems require careful consideration of all possible order of executions, possible instruction re-ordering by compiler or CPU, and on certain obscure platforms even cache consolidation between cores.
Local variables cannot be declared volatile. That would not compile.
He should not use the keyword, use a half-fence memory barrier: ex Volatile.Read.
I think this would be a better answer if it actually explained what volatile does (other than "makes the program work").
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.