.NET 中的執行緒/并發/記憶體模型專家,您能否驗證以下代碼在所有情況下(即,無論作業系統、.NET 運行時、CPU 架構等)是否正確?
class SomeClassWhoseInstancesAreAccessedConcurrently
{
private Strategy _strategy;
public SomeClassWhoseInstancesAreAccessedConcurrently()
{
_strategy = new SomeStrategy();
}
public void DoSomething()
{
Volatile.Read(ref _strategy).DoSomething();
}
public void ChangeStrategy()
{
Interlocked.Exchange(ref _strategy, new AnotherStrategy());
}
}
這種模式經常出現。我們有一個由多個執行緒同時使用的物件,并且在某些時候需要更改其中一個欄位的值。我們希望保證從那時起,來自任何執行緒的對該欄位的每次訪問都會觀察到新值。
考慮上面的例子,我們要確保在ChangeStrategy執行的時間點之后,不會發生SomeStrategy.DoSomething被呼叫的情況,而不是AnotherStrategy.DoSomething因為一些執行緒沒有觀察到變化并使用快取在暫存器中的舊值/CPU 快取/隨便。
據我對該主題的了解,我們至少需要 volatile read 來防止這種快取。主要問題是這是否足夠或者我們需要Interlocked.CompareExchange(ref _strategy, null, null)實作正確的行為?
如果 volatile 讀取就足夠了,就會出現另一個問題:Interlocked.Exchange在這種情況下,我們是否需要甚至 volatile 寫入都可以?
據我了解,易失性讀/寫使用半柵欄,它允許一個 write 后跟一個 read reordered,老實說,我仍然無法完全理解其含義。但是,根據 ECMA 335 規范,第 I.12.6.5 節,“類別庫在 System.Threading.Interlocked 類中提供了各種原子操作。這些操作(例如,增量、減量、交換和比較交換)執行隱式獲取/釋放操作。” 所以,如果我理解正確的話,Interlocked.Exchange應該創建一個完整的圍欄,看起來就足夠了。
但是,更復雜的是,似乎并非所有Interlocked操作都按照每個平臺上的規范實作。
如果有人能解決這個問題,我將不勝感激。
uj5u.com熱心網友回復:
是的,您的代碼是安全的。它在功能上等同于使用lock這樣的:
public void DoSomething()
{
Strategy strategy;
lock (_locker) strategy = _strategy;
strategy.DoSomething();
}
public void ChangeStrategy()
{
Strategy strategy = new AnotherStrategy();
lock (_locker) _strategy = strategy;
}
不過,您的代碼性能更高,因為它lock施加了一個完整的柵欄,而Volatile.Read施加了一個可能更便宜的半柵欄。
您可以通過用(半柵欄)替換Interlocked.Exchange(全柵欄)來進一步提高性能。喜歡 的Volatile.Write唯一原因Interlocked.Exchange是Volatile.Write當您想將先前的策略檢索為原子操作時。顯然,您的情況不需要這樣做。
為簡單起見,您甚至可以去掉Volatile.Write/Volatile.Read呼叫,只需將_strategy欄位宣告為volatile.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/520376.html
