2012-05-10 37 views
3

線程安全嗎?C#原子中的基本算術運算

例如,如果在全局變量上存在++操作,將從不同線程修改該變量,是否需要鎖定它?

例如

void MyThread() // can have many running instances 
{ 
    aGlobal++; 
} 

還是應

void MyThread() 
{ 
    lock(lockerObj) 
    { 
     aGlobal++; 
    } 
} 
+4

這不是一個獨特的算術運算......也有一個寫入變量! 'a + a'與'a = a + a'不同。你必須使用'Interlocked.Increment'或者相同! –

回答

8

The spec概括起來很好。第5.5節「變量引用的原子」:

讀取和下列數據類型的寫入是原子:布爾,焦炭, 字節,爲sbyte,短,USHORT,UINT,整數,浮點,和引用類型。另外,在 之前的列表中,對基類型的枚舉類型的讀寫操作也是原子的。其他類型的讀取和寫入, 包括long,ulong,double和decimal,以及用戶定義的 類型,不保證是原子性的。除了爲此目的而設計的函數之外,不能保證讀取 - 修改 - 寫入原子,例如在遞增或遞減的情況下。

結論:

  • 獨立的讀/寫是原子(但僅限於一些數據類型)
  • 讀取/修改/寫(如i++)是從未原子
  • 你可以使用Interlocked類方法在尚未保證的情況下實現原子性

Interlocked功能不夠的情況下,除了使用同步原語(例如Monitor.Enter(編譯器還通過lock語句公開))之外沒有別的選擇。

2

讀寫獨立在大多數類型(不是更長的類型,通常是64位+)上是原子的。但是你想要讀取,更改,然後寫入原子 - 這絕對是而不是原子。

如果您需要增加一個值,則有System.Threading.Interlocked類,其中有靜態IncrementDecrement操作。

如果你需要做更復雜的總和,那麼使用lock或其他構造的同步是唯一的方法。這或者使消息系統中的事物不可變,這樣您就不會遇到任何共享數據訪問問題,但除非設計爲預先提供,否則通常無法實現。

0

既不......你應該去System.Threading.Interlocked.Increment(ref int location)來代替。這是無鎖的,因爲它確保處理器以正確的順序執行讀取和寫入(指令 - 原子) - 而不使用System.Threading.Interlocked.Increment爲處理器提供重新排序指令的機會。

做大錘(或沒有其他可能性,因爲您可能正在做大量的操作),您也可以使用lock - 但即使在這些情況下,我寧願去System.Threading.ReaderWriterLockSlim來代替。

btw - a nice read