2016-06-07 54 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