Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# memory model and non volatile variable initialized before the other thread creation

I have a question related to the C# memory model and threads. I am not sure if the following code is correct without the volatile keyword.

public class A {
  private int variableA = 0;

  public A() {

    variableA = 1;

    Thread B = new Thread(new ThreadStart(() => printA())).Start();
  }

  private void printA() {
    System.Console.WriteLine(variableA);
  }
}

My concern is if it is guaranteed that the Thread B will see variableA with value 1 without using volatile? In the main thread I am only assigning 1 to variableA in the constructor. After that I am not touching variableA, it is used only in the Thread B, so locking is probably not necessary.

But, is it guaranteed that the main thread will flush his cache and write the variableA contents to the main memory, so the second thread can read the newly assigned value?

Additionally, is it guaranteed that the second thread will read the variableA contents from the main memory? May some compiler optimizations occur and the Thread B can read the variableA contents from the cache instead of the main memory? It may happen when the order of the instructions is changed.

For sure, adding volatile to the variableA declaration will make the code correct. But, is it neccessary? I am asking because I wrote some code with some non volatile variables initialization in the constructor, and the variables are used later by some Timer threads, and I am not sure if it is totally correct.

What about the same code in Java?

Thanks, Michal

like image 761
Michał Fronczyk Avatar asked Aug 29 '10 16:08

Michał Fronczyk


2 Answers

There are a lot of places where implicit memory barriers are created. This is one of them. Starting threads create full barriers. So the write to variableA will get committed before the thread starts and the first reads will be acquired from main memory. Of course, in Microsoft's implementation of the CLR that is somewhat of a moot point because writes already have volatile semantics. But the same guarentee is not made in the ECMA specification so it is theorectically possible that the Mono implemenation could behave differently in this regard.

My concern is if it is guaranteed that the Thread B will see variableA with value 1 without using volatile?

In this case...yes. However, if you continue to use variableA in the second thread there is no guarentee after the first read that it will see updates.

But, is it guaranteed that the main thread will flush his cache and write the variableA contents to the main memory, so the second thread can read the newly assigned value?

Yes.

Additionally, is it guaranteed that the second thread will read the variableA contents from the main memory?

Yes, but only on the first read.

For sure, adding volatile to the variableA declaration will make the code correct. But, is it neccessary?

In this very specific and narrow case...no. But, in general it is advised that you use the volatile keyword in these scenarios. Not only will it make your code thread-safe as the scenario gets more complicated, but it also helps to document the fact that the field is going to be used by more than one thread and that you have considered the implications of using a lock-free strategy.

like image 160
Brian Gideon Avatar answered Sep 29 '22 23:09

Brian Gideon


The same code in Java is definitely okay - the creation of a new thread acts as a sort of barrier, effectively. (All actions earlier in the program text than the thread creation "happen before" the new thread starts.)

I don't know what's guaranteed in .NET with respect to new thread creation, however. Even more worrying is the possibility of a delayed read when using Control.BeginInvoke and the like... I haven't seen any guarantees around memory barriers for those situations.

To be honest, I suspect it's fine. I suspect that anything which needs to coordinate between threads like this (either creating a new one or marshalling a call onto an existing one) will use a full memory barrier on both of the threads involved. However, you're absolutely right to be concerned, and I'm hoping that you'll get a more definitive answer from someone smarter than me. You might want to email Joe Duffy to get his point of view on this...

like image 35
Jon Skeet Avatar answered Sep 30 '22 00:09

Jon Skeet