2016-06-07 55 views
5

在Java中,如果仅在加入变体的线程后才访问该字段,则该字段不必是易失性的;该联合在关系之前强制执行。如果我调用Thread.Join(),是否需要volatile?

在c#中怎么样?使用下面的代码,我保证在调用Join()之后看到_value的更新值,还是需要使_value易变?

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"); 
} 

在我的代码片段中,会不会打印“BAR”?

+0

您不需要为该代码进行任何同步(除'.Join()')之外。即使没有它,你也不需要使用'volatile',因为改变一个字符串引用是一个原子操作,所以没有机会观察到一个残缺的值。 –

+0

@MatthewWatson,你的意思是说,因为Join(),主线程保证会获取由其他线程设置的_value的新值? – AfterWorkGuinness

+0

是的,另一个线程在'.Join()'返回后会终止。 –

回答

1

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

这些保证通常没有记录,但实际上很明显。唉,我不能提供确凿的证据,只能说别的什么都会发疯。

请参阅this list作为证据证明这没有很好的记录,并且您正在寻找的财产很有可能存在。

在我的代码片段中,会不会打印“BAR”?

是的。我相信所有的专家都会同意这一点。这里有一个简单的代码示例,使相同点:

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. 
+0

它显示字符串输出的顺序。控制台已同步。因此,这是全球订购的良好考验。如果您更喜欢这样的话,可以使用全局替代。 @sstan – usr

+0

假设加入时发生了障碍,2,1怎么会被打印? (它不能) – usr

+0

请注意,这是保证工作,因为Console.WriteLine是写入“外部资源”。 –

3

第一关:我的一般经验法则是,如果我要问这个问题:“这是否需要挥发?”那么我就不太了解内存模型,不足以编写低锁定代码。

只要把这东西放在一个锁,不要试图低锁密码非常好理由和专家的建议。

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

为了解决你的实际问题:

我保证可以看到调用join()后_value的更新值或做我需要做_value挥发性?

回答你的问题是,在C#规范,这是我在这里引用为了您的方便:

一个C#程序

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

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

+0

不错,所以这是指定毕竟。尽管许多谜团仍然存在于其他常见的基元中(而不是这个问题)。规范中的这个列表是热石上的一滴。 – usr