2010-05-16 39 views
12

我最近閱讀關於內存障礙和重新排序問題,現在我對它有一些困惑。鎖定語句的內存屏障

考慮以下情形:

private object _object1 = null;  
private object _object2 = null; 
private bool _usingObject1 = false; 

private object MyObject 
{ 
    get 
    { 
     if (_usingObject1) 
     { 
      return _object1; 
     } 
     else 
     { 
      return _object2; 
     } 
    } 
    set 
    { 
     if (_usingObject1) 
     { 
      _object1 = value; 
     } 
     else 
     { 
      _object2 = value; 
     } 
    } 
} 

private void Update() 
{ 
    _usingMethod1 = true; 
    SomeProperty = FooMethod(); 
    //.. 
    _usingMethod1 = false; 
} 
  1. Update方法;是在獲取或設置屬性之前總是執行的_usingMethod1 = true語句?或由於重新排序問題,我們無法保證?

  2. 我們應該用volatile

    private volatile bool _usingMethod1 = false; 
    
  3. 如果我們使用lock;我們可以保證,則鎖內的每個語句爲了等執行:

    private void FooMethod() 
    { 
        object locker = new object(); 
        lock (locker) 
        { 
         x = 1; 
         y = a; 
         i++; 
        } 
    } 
    

回答

28

記憶障礙的主題相當複雜。它甚至不時絆倒專家。當我們談論記憶障礙時,我們真的將兩種不同的想法結合起來。

  • 採集圍欄:一個存儲器屏障,其中其他讀取&寫入不允許籬笆之前移動
  • 釋放柵欄:其他讀取&寫入的內存柵欄不允許在柵欄後移動

僅創建兩個中的一個的內存屏障有時被稱爲半圍欄。建立兩者的記憶障礙有時被稱爲全圍欄

volatile關鍵字創建了半欄。易失性字段的讀取具有語義,而寫入具有釋放語義。這意味着在讀取之前或寫入之後不可以移動指令。

lock關鍵字在兩個邊界(入口和出口)上創建全圍欄。這意味着不能在每個邊界之前或之後移動指令。

但是,如果我們只關心一個線程,所有這些都沒有意義。訂單,因爲它被這個線程所感知,總是被保留下來。事實上,沒有這種基本的保證,任何程序都無法正常工作。真正的問題是線程如何感知讀取和寫入。這是你需要關心的地方。

因此,要回答你的問題:

  1. 從單一線程的角度來看......是的。從另一個線程的角度來看......不。

  2. 這取決於。這可能會奏效,但我需要更好地理解你想要達到的目標。

  3. 從另一個線程的角度來看......沒有。讀寫操作可以在鎖的邊界內自由移動。他們不能超越這些界限。這就是爲什麼其他線程也會造成內存障礙的原因。

+0

感謝您的信息,它真的幫助我更多地理解這個概念。我需要的是確保「_usingMethod1 = true」指令將在下一條指令SomeProperty = FooMethod()之前完成。在多線程塞加里奧如何實現這一目標?是通過:_usingMethod1 = true; Thread.MemoryBarrier(); SomeProperty = FooMethod();或鎖定完整的柵欄,所以沒有reordaring:lock(locker){_usingMethod1 = true; } SomeProperty = FooMethod();或者可能只是通過使_usingMethod1成爲一個易變的變量。謝謝你的幫助。 – 2010-05-17 08:59:38

+2

我會將Update方法的全部內容封裝在一個鎖中。除了記憶障礙之外,它還保證原子性,這同樣重要。另外,這些無鎖定的習慣用法(通過volatile,Thread.MemoryBarrier等)難以想象。 – 2010-05-17 13:25:25

4

揮發性關鍵詞在這裏沒有完成任何事情它有很弱的保證,它並不意味着記憶障礙。您的代碼不會顯示其他線程正在創建,因此很難猜測是否需要鎖定。然而,如果兩個線程可以同時執行Update()並使用同一個對象,這是一個很難的要求。

請注意,您發佈的鎖碼不會鎖定任何內容。每個線程都有自己的「locker」對象實例。您必須將其設爲您的類的私有字段,由構造函數或初始化程序創建。因此:

private object locker = new object(); 

private void Update() 
{ 
    lock (locker) 
    { 
     _usingMethod1 = true; 
     SomeProperty = FooMethod(); 
     //.. 
     _usingMethod1 = false; 
    } 
} 

請注意,SomeProperty任務也會出現競爭。

+0

易失性存儲器有障礙;所以我問是否與它有關的momory障礙會抑制reords,所以_usingMethod1 = true會在獲取或設置屬性之前總是保證執行SomeProperty, 我提供了一個鎖,用於存儲障礙而不是與其他線程同步問題等我將它作爲目的方法中的局部變量,因爲我問是否會避免鎖內指令的重新指定。 – 2010-05-17 08:31:34

+1

單線程* always *對其使用的變量具有一致的視圖。如果情況並非如此,程序無法工作。 – 2010-05-17 10:20:41