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

C# 关于SyncRoot模式的一些说明:使用此模式的正确方法是什么?,c#,.net,multithreading,thread-safety,C#,.net,Multithreading,Thread Safety,我读了一些关于SyncRoot模式的文章,作为避免死锁的一般规则。读了几年前的一个问题(见此),我想我理解这种模式的一些用法可能是不正确的。特别是,我着重于以下句子: 您将在System.Collections中的许多集合上注意到SyncRoot属性。在回顾中,我认为这是一个 错误请放心,我们不会在建造时犯同样的错误 这些集合的通用版本 事实上,例如,List类没有实现SyncRoot属性,或者更准确地说,它是显式实现的(请参见),因此必须强制转换到ICollection,才能使用它。但认为将私

我读了一些关于SyncRoot模式的文章,作为避免死锁的一般规则。读了几年前的一个问题(见此),我想我理解这种模式的一些用法可能是不正确的。特别是,我着重于以下句子:

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

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

因此,如果我理解正确,当我实现一个非线程安全的数据结构时,因为它可以在多线程上下文中使用,所以我不应该(实际上,我不能)提供
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
        }
    }
}
总之,我理解同步/锁定的最佳实践应如下所示:

  • 任何私有SyncRoot对象都不应通过公共属性公开;换句话说,自定义数据结构不应提供公共
    SyncRoot
    属性(另请参见)
  • 通常,不强制使用私有对象进行锁定(请参见)
  • 如果一个类有多组需要同步的操作,但不能相互同步,那么它应该有多个私有SyncRoot对象(请参阅)

  • 上面写的是正确使用此模式的内容吗?

    这里有许多概念。首先,您正确实现的是线程安全类,该类的使用者不需要自己进行同步。因此,绝对不需要公开syncRoot对象。在旧的集合类中,SyncRoot属性是公开的,因为这些类不是线程安全的

    锁定任意对象和锁定内部集合时,程序的正确性或性能绝对没有区别。只要对两者的引用不发生变化,它们的工作原理与Monitor.Enter/Exit的参数一样。你的内部收藏会改变吗?如果否,也将其标记为只读


    第三是关于根据不同操作使用不同锁的评论。这方面的经典示例是ReaderWriterLock。您应该根据类公开的不同功能分析使用不同级别锁的需要。

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

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

    • 这种类型变得更胖:它的职责变得更加分散。为什么我要添加对显式多线程锁定的支持而不支持其他漏洞?我会强迫用户只遵循一种实践,这可能不是所有情况下的最佳解决方案

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

      public object SyncRoot {get {return this;}}
      

    我还避免使用.NET framework类型中的
    SyncRoot
    属性。如果我需要将类型设置为不带SyncRoot属性的线程安全类型,我将使用一种锁定模式,如果类型具有此属性,我仍然不会选择在SyncRoot上锁定。这使我的代码风格保持一致,并且更易于阅读/维护。

    我的观点是,如果你想让一个类在内部实现线程安全,你需要在内部(私下)进行锁定,并记录该类通常是线程安全的。否则,请不要锁定,记录该类不是线程安全的,并让类的使用者根据其特定用途的需要在其周围实现自己的锁定。