為什麼實現 .NET 的 ICollection 集合時需要實現 SyncRoot 屬性?如何正確實現這個屬性?
- 2020 年 1 月 8 日
- 筆記
非泛型版本的 ICollection
中有 IsSynchronized
屬性和 SyncRoot
屬性,這兩個屬性被用來設計成以執行緒安全的方式訪問和修改集合。不過這個設計讓執行緒安全的訪問有集合的實現方轉嫁到了調用方,導致要麼很難實現,要麼很難調用。
雖然泛型版本的 ICollection<T>
已經改進了設計,不再引入 SyncRoot
這樣的屬性到介面中,但如果我們在某些場景下需要實現 ICollection
非泛型集合時,如何正確實現 SyncRoot 模式(SyncRoot Pattern)呢?
先上結論:
—— 不可能正確實現 SyncRoot 模式
在多執行緒程式設計中,為了在保證執行緒安全的同時避免死鎖,不應該公開同步鎖。而 ICollection
介面中的 SyncRoot
屬性在介面中必然是公開的,於是沒有任何途徑可以保證調用方不會發生死鎖。
於是實現 SyncRoot
的正確方法應該是:
—— 避免公開 SyncRoot 屬性
所以 SyncRoot 模式應該這樣實現:
- 使用顯式介面實現,避免公開暴露此屬性
- 拋出異常,避免調用者使用此屬性
結合 .NET Core 源程式碼中的一些常用寫法,我給出一個推薦的 SyncRoot 模式的寫法:
// Is this List synchronized (thread-safe)? bool ICollection.IsSynchronized => false; // Synchronization root for this object. object ICollection.SyncRoot => this;
嗯,沒錯,返回了 this
,這是各種同步時絕對不應該使用的對象。然而這個屬性都是 public
了,不管返回什麼,與 this
還有什麼區別……
關於為什麼同步時不應該返回 this
或者返回公開的對象,原因可以看我的另一篇部落格: