Why volatile and MemoryBarrier do not prevent operations reordering?
.net, c#, multithreading
Solution
I don't think this is re-ordering.
This piece of code is simply not thread-safe:
while (continueChecking)
{
int tempA = a;
int tempB = b;
...
I think this scenario is possible:
- `int tempA = a;` executes with the values of the last loop (a == 2)
- There is a context switch to the Write thread
- `b = 10` and the loop stops
- There is a context switch to the Check thread
- `int tempB = b;` executes with b == 10
I notice that the calls to MemoryBarrier() enhance the chances of this scenario. Probably because they cause more context-switching.
Problem
If I understand meaning of volatile and MemoryBarrier correctly than the program below has never to be able to show any result. It catches reordering of write operations every time I run it. It does not matter if I run it in Debug or Release. It also does not matter if I run it as 32bit or 64bit application. Why does it happen? ``` using System; using System.Threading; using System.Threading.Tasks; namespace FlipFlop { class Program { //Declaring these variables as volatile should instruct compiler to //flush all caches from registers into the memory. static volatile int a; static volatile int b; //Track a number of iteration that it took to detect operation reordering. static long iterations = 0; static object locker = new object(); //Indicates that operation reordering is not found yet. static volatile bool continueTrying = true; //Indicates that Check method should continue. static volatile bool continueChecking = true; static void Main(string[] args) { //Restarting test until able to catch reordering. while (continueTrying) { iterations++; var checker = new Task(Check); var writter = new Task(Write); lock (locker) { continueChecking = true; checker.Start(); } writter.Start(); checker.Wait(); writter.Wait(); } Console.ReadKey(); } static void Write() { //Writing is locked until Main will start Check() method. lock (locker) { //Using memory barrier should prevent opration reordering. a = 1; Thread.MemoryBarrier(); b = 10; Thread.MemoryBarrier(); b = 20; Thread.MemoryBarrier(); a = 2; //Stops spinning in the Check method. continueChecking = false; } } static void Check() { //Spins until finds operation reordering or stopped by Write method. while (continueChecking) { int tempA = a; int tempB = b; if (tempB == 10 && tempA == 2) { continueTrying = false; Console.WriteLine("Caught when a = {0} and b = {1}", tempA, tempB); Console.WriteLine("In " + iterations + " iterations."); break; } } } } } ```