我读了一些关于 SyncRoot模式的内容,作为避免死锁的一般规则。阅读几年前的问题(见link),我想我明白这种模式的一些用法可能是不正确的。特别是,我专注于this topic中的以下句子:
您会注意到System.Collections中许多集合的SyncRoot属性。在回顾中,我认为这个属性是一个 错误......请放心,我们不会犯同样的错误 这些集合的通用版本。
事实上,例如,List<T>
类不实现SyncRoot
属性,或者更正确地实现它(请参阅this answer),因此必须强制转换为{{1为了使用它。但是this comment认为私有ICollection
字段公开与锁定SyncRoot
一样糟糕(请参阅this answer),同样在this comment中也已确认。 / p>
因此,如果我理解正确,当我实现非线程安全的数据结构时,由于它可以在多线程上下文中使用,我不应该(实际上,我不能)提供this
属性。但是我应该让开发人员(将使用此数据结构)的任务是将其与私有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
}
}
}
属性(另请参阅this comment)。上面写的正确使用了这种模式吗?
答案 0 :(得分:4)
我会避免在我设计的类型中添加SyncRoot
属性,原因如下:
我的类型的用户可能需要使用不同的同步机制,例如Mutex
或ReaderWriterLock
或ReaderWriterLockSlim
等
这种类型变得更胖:它的责任变得更加分散。为什么我会添加对显式多线程锁定的支持而不支持其他的多线程?我会强迫用户只遵循一种做法,这可能不是所有情况下的最佳解决方案
我需要正确实现该属性(不返回this
或typeof(MyClass)
),即这是错误的:
public object SyncRoot {get {return this;}}
我也会避免使用.NET框架类型中的SyncRoot
属性。如果我需要创建一个没有SyncRoot
属性线程安全的类型,我将使用一个锁定模式,如果类型具有此属性,我仍然不会选择锁定SyncRoot
。这使我的代码风格一致且易于阅读/维护。
答案 1 :(得分:0)
这里有很多概念。首先,您正确实现的是一个线程安全的类,其中没有类的消费者需要自己进行同步。因此,您绝对不需要公开syncRoot对象。在旧的Collection类中,暴露了SyncRoot属性,因为类不是线程安全的。
在锁定任意对象并锁定内部集合时,在程序的正确性或性能方面绝对没有区别。只要对两者的引用都没有改变,它们的工作方式与Monitor.Enter / Exit的参数一样好。你的内心收藏会改变吗?如果不是,请将其标记为只读。
第三,有关于基于不同操作使用不同锁的评论。典型的例子是ReaderWriterLock。您应该根据类的不同功能分析是否需要使用不同级别的锁。