.NET Framework ReaderWriterLock類相關概念詳解
.NET Framework提供了一個非常好的開發(fā)環(huán)境,為開發(fā)人員帶來了非常大的好處。在這里就先來了解一下有關.NET Framework ReaderWriterLock類的相關概念介紹。.NET Framework BCL 在 1.1 版本時,給我們提供了一個.NET Framework ReaderWriterLock類來面對此種情景。但是很遺憾,Microsoft 官方不推薦使用該類。Jeffrey Richter 也在他的《CLR via C#》一書中對它進行了嚴厲的批判。下面是該類不受歡迎的主要原因:#t#
性能
.NET Framework ReaderWriterLock類實在是太慢了。比如它的 AcquireReaderLock 方法比 Monitor 類的 Enter 方法要慢 5 倍左右,而等待爭奪寫鎖甚至比 Monitor 類慢 6 倍。
策略
假如某個線程完成寫入操作后,同時面臨讀線程和寫線程等待處理。ReaderWriterLock 會優(yōu)先釋放讀線程,而讓寫線程繼續(xù)等待。但我們使用讀寫鎖是因為存在大量的讀線程和非常少的寫線程,這樣寫線程很可能必須長時間地等待,造成寫線程饑餓,不能及時更新數(shù)據(jù)。更槽糕的情況是,假如寫線程一直等待,就會造成活鎖。反之,我們讓 ReaderWriterLock 采取寫線程優(yōu)先的策略。如果存在多個寫線程,而讀線程數(shù)量稀少,也會造成讀線程饑餓。幸運的是,現(xiàn)實實踐中,這種情況很少出現(xiàn)。一旦發(fā)生這種情況,我們可以采取互斥鎖的辦法。
遞歸
.NET Framework ReaderWriterLock類支持鎖遞歸。這就意味著該鎖清楚的知道目前哪個線程擁有它。假如擁有該鎖的線程遞歸嘗試獲得該讀寫鎖,遞歸算法允許該線程獲得該讀寫鎖,并且增加獲得該鎖的計數(shù)。然而該線程必須釋放該鎖相同的次數(shù)以便線程不再擁有該鎖。盡管這看起來是個很好的特性,但是實現(xiàn)這個“特性”代價太高。首先,因為多個讀線程可以同時擁有該讀寫鎖,這必須讓該鎖為每個線程保持計數(shù)。
此外,還需要額外的內存空間和時間來更新計數(shù)。這個特性對 ReaderWriterLock 類可憐的性能貢獻極大。其次,有些良好的設計需要一個線程在此處獲得該鎖,然后在別處釋放該鎖(比如 .NET 的異步編程架構)。因為這個遞歸特性,ReaderWriterLock 不支持這種編程架構。
資源泄漏
在 .NET 2.0 之前的版本中, ReaderWriterLock 類會造成內核對象泄露。這些對象只有在進程終止后才能再次回收。幸運的是,.NET 2.0 修正了這個 Bug 。
此外,ReaderWriterLock 還有個令人擔心的危險的非原子性操作。它就是 UpgradeToWriteLock 方法。這個方法實際上在更新到寫鎖前先釋放了讀鎖。這就讓其他線程有機會在此期間乘虛而入,從而獲得讀寫鎖且改變狀態(tài)。如果先更新到寫鎖,然后釋放讀鎖。假如兩個線程同時更新將會導致另外一個線程死鎖。
所以 Microsoft 決定構建一個新類來一次性解決上述所有問題,這就是 ReaderWriterLockSlim 類。本來可以在原有的.NET Framework ReaderWriterLock類上修正錯誤,但是考慮到兼容性和已存在的 API ,Microsoft 放棄了這種做法。當然也可以標記 ReaderWriterLock 類為 Obsolete,但是由于某些原因,這個類還有存在的必要。