為什麼實現 .NET 的 ICollection 集合時需要實現 SyncRoot 屬性?如何正確實現這個屬性?

非泛型版本的 ICollection 中有 IsSynchronized 屬性和 SyncRoot 屬性,這兩個屬性被用來設計成以線程安全的方式訪問和修改集合。不過這個設計讓線程安全的訪問有集合的實現方轉嫁到了調用方,導致要麼很難實現,要麼很難調用。

雖然泛型版本的 ICollection<T> 已經改進了設計,不再引入 SyncRoot 這樣的屬性到接口中,但如果我們在某些場景下需要實現 ICollection 非泛型集合時,如何正確實現 SyncRoot 模式(SyncRoot Pattern)呢?


先上結論:

—— 不可能正確實現 SyncRoot 模式

在多線程程序設計中,為了在保證線程安全的同時避免死鎖,不應該公開同步鎖。而 ICollection 接口中的 SyncRoot 屬性在接口中必然是公開的,於是沒有任何途徑可以保證調用方不會發生死鎖。

於是實現 SyncRoot 的正確方法應該是:

—— 避免公開 SyncRoot 屬性

所以 SyncRoot 模式應該這樣實現:

  1. 使用顯式接口實現,避免公開暴露此屬性
  2. 拋出異常,避免調用者使用此屬性

結合 .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 或者返回公開的對象,原因可以看我的另一篇博客: