如果我调用Thread.Join(),我是否需要volatile?

时间:2016-06-07 17:43:39

标签: c# concurrency volatile

在Java中,如果只有在加入突变它的线程后访问它,那么该字段不需要是易失性的;联接在关系之前强制执行。

在c#中怎么样?使用下面的代码,我保证在调用Join()之后会看到_value的更新值,或者我是否需要使_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");
}

在我的代码段中,&#34; BAR&#34;总是打印?

2 个答案:

答案 0 :(得分:3)

首先:我的一般经验法则是,如果我问的问题是“这需要变动吗?”那时我不太了解内存模型,不足以编写低锁代码。

只需将物品置于锁定之下,如果没有非常充分的理由和专家的建议,请不要尝试低锁定代码。

我不是这样的专家。我不太了解C#内存模型编写低锁代码,如果在弱内存模型硬件上运行它将是正确的。

解决您的实际问题:

  

我保证在调用Join()之后会看到_value的更新值,还是我需要使_value变为volatile?

您的问题的答案在C#规范中,为方便起见,我在此引用:

  

执行C#程序,以便在关键执行点保留每个执行线程的副作用。副作用定义为易失性字段的读取或写入,对非易失性变量的写入,对外部资源的写入以及抛出异常。必须保留这些副作用的顺序的关键执行点是对volatile字段,锁定语句以及线程创建和终止的引用。

您有一个非易失性变量的写入,并且该线程在连接返回时结束,因此必须在连接点保留写入的副作用。

答案 1 :(得分:1)

通用线程同步操作执行完整的内存屏障。肯定是开始和加入并结束一个线程。没有这些,各种程序都会出现故障。

这些保证通常没有记录,但实际上很明显。唉,我不能提供确凿的证据,除非说其他任何东西都会疯狂。

请参阅this list作为未充分记录的证据,并且您正在寻找的财产很可能成立。

  

在我的代码段中,&#34; BAR&#34;总是打印?

是。我相信所有专家都会就此达成一致。这是一个更简单的代码示例,它提出了相同的观点:

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.