Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do I need volatile if I call Thread.Join()?

In Java, a field doesn't need to be volatile if you access it only after joining on the thread that mutated it; the join enforces a happens before relationship.

What about in c#? With the below code, am I guaranteed to see the updated value of _value after calling Join() or do I need to make _value volatile ?

private String _value = "FOO"

public void Foo() 
{

   Thread myThread = new Thread(Bar);
   myThread.Start();
   myThread.Join();
   Console.WriteLine("[Main Thread] _val = "+ _value);

}

public void Bar()
{

   for(int i=0; i<1000; i++)
   {
         Console.WriteLine("Looping");

         if(i==75) 
         {
             _value="BAR";
         }
   }
   Console.WriteLine("DONE Looping");
}

In my code snippet, will "BAR" always be printed?

like image 752
AfterWorkGuinness Avatar asked Jun 07 '16 17:06

AfterWorkGuinness


2 Answers

First off: My general rule of thumb is if I am asking the question "does this need to be volatile?" then I do not understand the memory model well enough to be writing low-lock code.

Just put the thing under a lock and do not attempt low-lock code without an extraordinarily good reason and the advice of an expert.

I am not such an expert. I don't know nearly enough about the C# memory model to write low-lock code with any confidence that it will be correct if run on weak memory model hardware.

To address your actual question:

am I guaranteed to see the updated value of _value after calling Join() or do I need to make _value volatile ?

The answer to your question is in the C# specification, which I quote here for your convenience:

Execution of a C# program proceeds such that the side effects of each executing thread are preserved at critical execution points. A side effect is defined as a read or write of a volatile field, a write to a non-volatile variable, a write to an external resource, and the throwing of an exception. The critical execution points at which the order of these side effects must be preserved are references to volatile fields, lock statements, and thread creation and termination.

You have a write to a non-volatile variable, and the thread is ended by the time the join returns, so the side effect of the write must be preserved at the point of the join.

like image 136
Eric Lippert Avatar answered Oct 23 '22 08:10

Eric Lippert


Common thread synchronization actions perform a full memory barrier. Start and Join and ending a thread surely are among them. Without that all kinds of programs would malfunction.

These guarantees often are not documented but are practically evident. Alas, I cannot provide hard evidence except to say that anything else would be crazy.

See this list as evidence that this is not documented well and that the property you are looking for very likely holds.

In my code snippet, will "BAR" always be printed?

Yes. I believe all experts would agree on that. Here's a simpler code sample that makes the same point:

int x = 0;
Thread myThread = new Thread(() => x = 1);
myThread.Start();
myThread.Join();
x = 2;
Console.WriteLine(x); //Prints 2 because of memory barriers on exit and on Join.
like image 34
usr Avatar answered Oct 23 '22 07:10

usr