Volatile, Interlocked: trying to break the code

First of all, I tried to find out if using Interlocked requires a volatile field definition, and this is my real question.

But. Being too lazy to analyze the generated MSIL, I decided to test this in practice.

I am trying an MSDN example for using volatile when code should break in a release build with optimizations. And nothing will break. The code works fine (in this case, the main thread finishes gracefully) with optimization turned on and off.

  • Should I still require the volatile keyword in the field when I write to it from one thread using Interlocked and read from another thread without blocking?
  • A simple code example from question 1, where does volatile make a difference?
  • Why does the MSDN example still work when I remove the volatile keyword and build in the release?

code snippet to illustrate question 1.

 class Example { volatile int val; void Do() { Task.Run(() => { while (val == 0) Console.WriteLine("running"); }); Thread.Sleep(1000); Interlocked.Increment(ref val); Console.WriteLine("done."); Console.ReadLine(); } } 
+6
source share
2 answers

If the variable is not marked as volatile , then the code reading it cannot know that it can be changed by any other thread. Therefore, the JIT compiler will not know that storing a value in a register is potentially dangerous.

volatile is how you tell the JIT compiler that other threads can change the value. That another thread may use Interlocked when a change in value does not matter, because JITER may not even know that another code exists!

Perhaps your simple example works because the JIT compiler can see all the code. Things can be different if these two bits of code were in separate methods or even in different assemblies in general.

+2
source

Well, I managed to create code where volatile really matters and Interlocked doesn't help. You must run the release assembly without a debugger in order to test it.

 public class Example { private volatile int val = 0; public static void Main() { var example = new Example(); Task.Run(() => Interlocked.Increment(ref example.val)); while (example.val == 0) ; // this never happens if val is not volatile Console.WriteLine("done."); Console.ReadLine(); } } 

I am going to accept @JimMischel's answer as it has some clarifications if nothing more detailed appears.

UPDATE: This blog post has also been found explaining some of the details.

+2
source

All Articles