【问题标题】:Some clarification on the SyncRoot pattern: what is the correct way to use this pattern?关于 SyncRoot 模式的一些说明:使用此模式的正确方法是什么?
【发布时间】:2012-09-14 12:53:15
【问题描述】:

我阅读了一些关于 SyncRoot 模式 的内容,作为避免死锁的一般规则。阅读几年前的一个问题(见link),我想我理解这种模式的某些用途可能是不正确的。特别关注this topic的以下句子:

您会注意到 System.Collections 中许多集合上的 SyncRoot 属性。回想起来,我认为该物业是 错误...请放心,我们不会犯与我们构建相同的错误 这些集合的通用版本。

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

所以,如果我理解正确,当我实现一个非线程安全的数据结构时,因为它可以在多线程上下文中使用,我不应该(实际上,我绝不能)提供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 属性(另请参阅this comment)。
  2. 一般来说,使用私有对象进行锁定不是强制性的(请参阅this answer)。
  3. 如果一个类有多组操作需要同步,但不能相互同步,则它应该有多个私有 SyncRoot 对象(请参阅this comment)。

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

【问题讨论】:

  • 我的观点是,如果你想让一个类在内部是线程安全的,你可以在内部(私下)进行锁定,并证明这个类通常是线程安全的。否则,请不加锁,证明该类不是线程安全的,并让您的类的使用者根据具体用途的需要实现自己的锁定。

标签: c# .net multithreading thread-safety


【解决方案1】:

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

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

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

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

    public object SyncRoot {get {return this;}}
    

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

【讨论】:

    【解决方案2】:

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

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

    第三,关于根据不同的操作使用不同的锁的注释。典型的例子是 ReaderWriterLock。您应该根据您的类公开的不同功能来分析使用不同级别的锁的需求。

    【讨论】:

      猜你喜欢
      • 2010-10-18
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 2016-12-16
      • 1970-01-01
      • 2017-11-16
      • 2016-01-09
      • 2015-06-05
      相关资源
      最近更新 更多