关于SyncRoot模式的一些说明:使用此模式的正确方法是什么?

我读了一些关于SyncRoot模式的内容,作为避免死锁的一般规则。 阅读几年前的问题(见此链接 ),我想我明白这种模式的一些用法可能是不正确的。 特别是,我专注于这个主题的以下句子:

您会注意到System.Collections中许多集合上的SyncRoot属性。 在回顾中,我认为这个属性是一个错误……请放心,我们不会犯这个错误,因为我们构建这些集合的通用版本。

实际上,例如, List类不实现SyncRoot属性,或者更准确地说它是显式实现的(请参阅此答案 ),因此必须强制转换为ICollection才能使用它。 但是这篇评论认为公开私有SyncRoot字段与锁定它一样糟糕(请参阅此答案 ),这也在本评论中得到证实。

因此,如果我理解正确,当我实现非线程安全的数据结构时,由于它可以在multithreading上下文中使用,我不应该(实际上,我不能)提供SyncRoot属性。 但是我应该让开发人员(将使用此数据结构)的任务是将其与私有SyncRoot对象相关联,如下面的示例代码所示。

 public class A { private MyNonThreadSafeDataStructure list; private readonly object list_SyncRoot = new object; public Method1() { lock(list_SyncRoot) { // access to "list" private field } } public Method2() { lock(list_SyncRoot) { // access to "list" private field } } } 

总之,我了解同步/锁定的最佳实践应如下所示:

  1. 不应通过公共属性公开任何私有SyncRoot对象; 换句话说,自定义数据结构不应提供公共SyncRoot属性(另请参阅此注释 )。
  2. 通常,不必使用私有对象进行锁定(请参阅此答案 )。
  3. 如果一个类有多组操作需要同步,但彼此之间没有,那么它应该有多个私有SyncRoot对象(请参阅此注释 )。

上面写的是正确使用这种模式的吗?

我会避免将SyncRoot属性添加到我设计的类型中,原因如下:

  • 我的类型的用户可能需要使用不同的同步机制,例如Mutex ,或ReaderWriterLockReaderWriterLockSlim

  • 这种类型变得更胖:它的责任变得更加分散。 为什么我会添加对显式multithreading锁定的支持而不支持其他绒毛? 我会强迫用户只遵循一种做法,这可能不是所有情况下的最佳解决方案

  • 我需要正确实现该属性(不返回thistypeof(MyClass) ),即这是错误的:

     public object SyncRoot {get {return this;}} 

我也会避免使用.NET框架类型的SyncRoot属性。 如果我需要创建一个没有SyncRoot属性线程安全的类型,我将使用一个锁定模式,如果类型具有此属性,我仍然不会选择锁定SyncRoot 。 这使我的代码风格一致且易于阅读/维护。

这里有很多概念。 首先,您正确实现的是一个线程安全的类,其中没有类的消费者需要进行自己的同步。 因此,您无需公开syncRoot对象。 在旧的Collection类中,暴露了SyncRoot属性,因为这些类不是线程安全的。

在锁定任意对象并锁定内部集合时,在程序的正确性或性能方面绝对没有区别。 只要对两者的引用都没有改变,它们的工作方式与Monitor.Enter / Exit的参数一样好。 你的内心collections会改变吗? 如果不是,请将其标记为只读。

第三,关于基于不同操作使用不同锁的评论。 这方面的经典例子是ReaderWriterLock。 您应该根据类的不同function分析是否需要使用不同级别的锁。