注意:此問題已被更改爲count
。對於當前的問題,而不是Thread.VolatileRead
我會用Interlocked.Read
,其中也有揮發性的語義,還將處理這裏討論和引入
原子的讀是沒有鎖定保障問題的64位讀的問題,因爲讀的正確對齊的32位或更小,這你count
是,保證是原子的值。
這與64位值不同,如果它從-1開始,並在另一個線程將其遞增時讀取,則可能導致讀取值爲-1(在增量之前發生),0(在增量之後發生)或者4294967295或-4294967296(32位寫入0,其他32位等待寫入)。
原子增量Interlocked.Increment
表示整個增量操作是原子的。考慮增量是概念上的:
- 閱讀值。
- 添加一個值。
- 寫入數值。
然後,如果x
是54和一個線程試圖增加它而另一嘗試將其設置爲67,這兩個正確可能的值是67(增量首先發生,然後被寫入結束)或68(分配發生第一,然後遞增),但非原子增量可能導致55(增量讀取,分配67發生,遞增寫入)。
更常見的真實情況是x
是54,一個線程遞增,另一個遞減。這裏唯一有效的結果是54(一個向上,然後一個向下,反之亦然),但如果不是原子的,那麼可能的結果是53,54和55.
如果你只是想要一個以原子方式遞增的計數,正確的代碼是:
private int count;
public int Count
{
get
{
return Thread.VolatileRead(byref count);
}
}
public void Increment
{
Interlocked.Increment(count);
}
如果不管你想採取什麼行動,那麼它需要更強的鎖定。這是因爲使用計數的線程在其操作完成之前可能會過時。在這種情況下,您需要鎖定所有關心計數以及改變計數的所有內容。究竟需要如何完成(以及它的重要性如何)取決於你的用例的更多事項,而不是從你的問題中推斷出來。
編輯:哦,你可能想鎖定只是爲了強制內存屏障。如果要刪除鎖,您可能還想將Count
的實現更改爲return Thread.VolatileRead(ref count);
以確保CPU緩存已刷新。這取決於在這種情況下緩存過期的重要性。 (另一種選擇是使count
易失性,因爲所有的讀寫操作都是不穩定的,請注意,Interlocked
操作不需要這些操作,因爲它們總是不穩定的。)
編輯2:的確,你是如此可能想要這種不穩定的閱讀,我正在改變上面的答案。它是可能你不會在意它提供什麼,但不太可能。
切勿鎖定'this',該鎖將與類鎖定對象之外的鎖相沖突。只鎖定私人引用類型的成員。如果你沒有一個適當的私有引用類型的成員,而不是正確類型的靜態或實例類型的成員,那麼就應該爲此目的設置'private object _myLock'''或'private static object _myStaticLock''。 – 2010-10-04 14:27:17
@Jon,注意和編輯 – 2010-10-04 14:30:13
我仍然有這樣的感覺,你可能會有比這個問題更大的魚來炒。就這個問題而言,我很滿意我的答案,但它並沒有處理線程A基於count進行某些操作的事情,而線程B更改了計數,因爲這是否是一個問題,以及如何處理它如果是,取決於你比這裏更多。 – 2010-10-04 14:37:52