線程安全嗎?C#原子中的基本算術運算
例如,如果在全局變量上存在++
操作,將從不同線程修改該變量,是否需要鎖定它?
例如
void MyThread() // can have many running instances
{
aGlobal++;
}
還是應
void MyThread()
{
lock(lockerObj)
{
aGlobal++;
}
}
線程安全嗎?C#原子中的基本算術運算
例如,如果在全局變量上存在++
操作,將從不同線程修改該變量,是否需要鎖定它?
例如
void MyThread() // can have many running instances
{
aGlobal++;
}
還是應
void MyThread()
{
lock(lockerObj)
{
aGlobal++;
}
}
The spec概括起來很好。第5.5節「變量引用的原子」:
讀取和下列數據類型的寫入是原子:布爾,焦炭, 字節,爲sbyte,短,USHORT,UINT,整數,浮點,和引用類型。另外,在 之前的列表中,對基類型的枚舉類型的讀寫操作也是原子的。其他類型的讀取和寫入, 包括long,ulong,double和decimal,以及用戶定義的 類型,不保證是原子性的。除了爲此目的而設計的函數之外,不能保證讀取 - 修改 - 寫入原子,例如在遞增或遞減的情況下。
結論:
i++
)是從未原子Interlocked
類方法在尚未保證的情況下實現原子性在Interlocked
功能不夠的情況下,除了使用同步原語(例如Monitor.Enter
(編譯器還通過lock
語句公開))之外沒有別的選擇。
讀寫獨立在大多數類型(不是更長的類型,通常是64位+)上是原子的。但是你想要讀取,更改,然後寫入原子 - 這絕對是而不是原子。
如果您需要增加一個值,則有System.Threading.Interlocked
類,其中有靜態Increment
和Decrement
操作。
如果你需要做更復雜的總和,那麼使用lock
或其他構造的同步是唯一的方法。這或者使消息系統中的事物不可變,這樣您就不會遇到任何共享數據訪問問題,但除非設計爲預先提供,否則通常無法實現。
既不......你應該去System.Threading.Interlocked.Increment(ref int location)
來代替。這是無鎖的,因爲它確保處理器以正確的順序執行讀取和寫入(指令 - 原子) - 而不使用System.Threading.Interlocked.Increment
爲處理器提供重新排序指令的機會。
做大錘(或沒有其他可能性,因爲您可能正在做大量的操作),您也可以使用lock
- 但即使在這些情況下,我寧願去System.Threading.ReaderWriterLockSlim
來代替。
btw - a nice read!
這不是一個獨特的算術運算......也有一個寫入變量! 'a + a'與'a = a + a'不同。你必須使用'Interlocked.Increment'或者相同! –