ICollection,只读集合和同步。这是正确的吗?

时间:2008-10-17 08:44:09

标签: c# .net-2.0

我有一个实现ICollection的自定义类,这个类是readonly,即。 IsReadOnly返回true(与使用readonly关键字相反),以及通常会修改集合中数据的所有函数抛出InvalidOperationException

现在,给定这样的构造,并在实现ICollection(特别是ICollection.IsSynchronized和朋友)时快速浏览线程安全问题,我提出了这个快速而肮脏的解决方案。

bool ICollection.IsSynchronised { get{ return true; } }
object ICollection.SyncRoot { get{ return new Object(); } }

现在,考虑到MSDN中的示例,这不会导致不同的线程正确锁定,因为它们从SyncRoot获取不同的对象。鉴于这是一个只读集合,这是一个问题吗?返回new Object()是否存在内存/ GC问题?您可以通过此实现看到任何其他问题吗?

3 个答案:

答案 0 :(得分:4)

是的,在某些情况下这是一个问题。即使集合是只读的并且无法更改,集合引用的对象也不是只读的。因此,如果客户端使用SyncRoot执行锁定,则在修改集合引用的对象时,它们将不是线程安全的。

我建议添加:

private readonly object syncRoot = new object();

到你的班级。将此作为SyncRoot返回,你很高兴。

答案 1 :(得分:2)

我想问题是如果客户端使用你的同步根来实现锁定你的集合,而不是其他东西。假设他们缓存了集合的大小 - 或者“这个集合的哪个子集匹配谓词” - 他们会合理地假设他们可以使用你的SyncRoot来保护你的集合和他们的其他成员。

就个人而言,我根本不使用SyncRoot,但我认为总是返回相同的东西是明智的。

答案 2 :(得分:2)

每次返回一个不同的对象似乎很奇怪......实际上,我很少(如果有的话)使用SyncRoot方法,因为我经常需要在多个对象之间进行同步,因此单独的对象更有意义。

但是如果数据真的是不可变的(readonly),为什么不从IsSynchronized返回false?

Re GC - 任何此类对象通常都是短暂的,并在GEN0中收集。如果你有一个带有对象的字段(对于锁),它会持续与集合一样长,但最有可能不会受到伤害......

如果你需要锁定,我很想得到一个:

private readonly object lockObj = new object();

你也可以在需要的时候使用懒惰的方法只是“新”它,如果你实际上不希望任何人要求同步锁(通过返回false到IsSynchronized),这有一定的意义。 / p>

另一种常见方法是返回“this”;它使事情变得简单,但是冒险与其他代码冲突,使用您的对象作为不相关目的的锁。很少见,但可能。这实际上是[MethodImpl]用于同步的方法。