構建ASP.NET線程安全集合淺析
大部分ASP.NET線程安全的集合都有一些基礎性的缺陷:雖然每個操作都是線程安全的,但是多個操作無法組合起來使用。這意味著一些基本的執(zhí)行順序,例如在彈出頂部元素之前檢查棧內元素數(shù)量會出現(xiàn)潛在的危險。盡管已經(jīng)有一些API設法將某些操作綁定起來(例如.NET 4的Coordination Data Structures),但是它們往往會引入丑陋的方法(如TryDequeue)。
.NET 1里的集合嘗試了另一種方式,它們會對外暴露一個SyncRoot屬性,而不是在內部進行鎖定。雖然SyncRoot仍然是同步對象的默認機制,但是.NET 2已經(jīng)拋棄了SyncRoot/Wrapper設計模式。
那么該如何創(chuàng)建一個可用的組合式API呢?Jared Parson認為集合不應該直接暴露出ASP.NET線程安全的API,所有的方法都應該屬于一個臨時的對象,而這個對象只有在您鎖定集合的時候才被創(chuàng)建出來。這個臨時對象是集合的“鑰匙”,只有鑰匙的持有者才能獲取集合內容。
以下示例為Jared Parsons的線程安全隊列:
- static void Example1(ThreadSafeQueue queue) {
- using (var locked = queue.Lock()) {
- if (locked.Count > 0) {
- var first = locked.Dequeue();
- }
- }
- }
名為locked的對象本身不是ASP.NET線程安全的,但是開發(fā)人員只有在using代碼塊中才能正確執(zhí)行操作。在遵守了這一簡單規(guī)則之后,開發(fā)塊里的所有代碼就是線程安全的。Jared解釋道:
與大部分ASP.NET線程安全的設計一樣,這些代碼還是有被誤用的可能:
1、在ILockedQueue銷毀之后卻繼續(xù)使用它。這種做法應該被禁止,用戶現(xiàn)有的知識一般足以避免這個問題。此外一些靜態(tài)檢查工具,例如FxCop,會把這種做法識別為一個錯誤。我們也可以使用一種更嚴厲的做法來阻止此類情況出現(xiàn):添加一個disposed標記,并在每個方法中進行檢查。
2、如果用戶在跨越多個Lock語句的情況下保留某個值(例如Count),那么可能會對集合的狀況出現(xiàn)錯誤的判斷和假設。
3、如果用戶沒有正確銷毀ILockedQueue,那么這個對象會被***鎖定。幸運的是,對于實現(xiàn)了IDisposable的對象,F(xiàn)xCop同樣會將這種做法識別為一個錯誤——盡管這不是一個萬分穩(wěn)妥的機制。
4、無法確定用戶是否會長期持有ILockedQueue對象。雖然IDisposable一般包含著“短期”的意味,但是這并不能做出***的保證。
5、ILockedQueue并不是線程安全的。雖然一般情況下用戶不會把IDisposable對象交給多個線程使用,但這也是必須考慮到的情況之一。
構建ASP.NET線程安全集合的相關信息就向你介紹到這里,希望對你有所幫助。
【編輯推薦】