简体   繁体   中英

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?

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM