在C#中執行線程同步時,我應該在讀取一個值還是僅僅改變它時鎖定一個對象?讀取值時應該鎖定資源嗎?
例如我有隊列<T>對象。我應該在執行Enqueue和Dequeue時將其鎖定,還是應該在檢查Count等值時鎖定它?
在C#中執行線程同步時,我應該在讀取一個值還是僅僅改變它時鎖定一個對象?讀取值時應該鎖定資源嗎?
例如我有隊列<T>對象。我應該在執行Enqueue和Dequeue時將其鎖定,還是應該在檢查Count等值時鎖定它?
從MSDN:
隊列<(中<(T>)>)可以支持 多個讀者同時,只要 作爲收集不被修改。 儘管如此,通過 集合列舉本質上不是線程安全的過程。爲了保證枚舉期間線程安全, 可以在整個枚舉過程中鎖定收集。爲了允許 集合被多個 線程讀取和寫入,您的 必須實現您自己的 同步。
你應該確保沒有閱讀器處於活動狀態,而一個項目排隊(鎖定可能是一個好主意)。
看反射器中的計數顯示從私人領域讀取。這可以取決於你對價值做什麼。這意味着你不應該做這樣的事情(沒有適當的鎖定):
if(queue.Count > 0)
queue.Dequeue();
取決於你想要用鎖來做什麼。通常這種鎖定需要一個讀寫器鎖定機制。
讀者/作者鎖定意味着讀者共享一個鎖,所以你可以讓多個讀者同時讀取這個集合,但是要寫,你應該獲得一個排他鎖。
CLR保證原子讀取的值高達處理器的寬度。所以如果你在32位上運行,閱讀int
將是原子性的。如果你在64位機器上運行,閱讀long
將是原子性的。 Ergo,如果Count
是Int32
則不需要鎖定。
This post與您的問題有關。
如果你沒有鎖定它,你可能會得到一個較舊的值。可能會發生競態條件,使得執行寫入操作時更改Count,但在更改之前您將獲得值。例如,如果隊列只有一個項目,並且一個線程調用了出隊,另一個線程可能會讀取該計數,找到它仍然爲1,並再次呼叫出隊。第二次調用在授予鎖定之前不會完成,但在那個時候隊列實際上是空的。
讀取int可能是原子的,但計算計數可能不一定是原子的。這不僅僅是讀一個整數。它需要一個函數調用,可能還有其他的東西。 – 2009-01-10 15:47:56
Count最可能是一個屬性,它對應於一個隱式函數調用。 – 2009-01-10 15:50:02