为什么.NET中没有通用的同步队列?
我注意到我们可以调用Queue.Synchronize来获取线程安全的队列对象,但是Queue <T>上没有相同的方法。有人知道为什么吗?似乎有点奇怪。
解决方案
(我假设意思是第二个队列<T>。)
除了IsSynchronized和SyncRoot属性(但不是显式的Synchronise())是从ICollection接口继承的,我无法具体回答该问题。没有一个通用集合使用此方法,并且ICollection <T>接口不包含SyncRoot。
至于为什么不包括在内,我只能推测它们没有按照图书馆设计者的意图使用,或者只是没有被充分利用以证明将它们保留在较新的馆藏中。
我们可能会发现Parallel CTP值得一试;这是来自那些正在整理它们的家伙的博客条目,这是非常热门的话题:
枚举并发集合
这不是完全一样的事情,但是它可以解决更大的问题。 (他们甚至使用" Queue <T>"和" ConcurrentQueue <T>"作为示例。)
在.NET 4中更新,System.Collections.Concurrent中现在有" ConcurrentQueue <T>",如此处http://msdn.microsoft.com/en-us/library/dd267265.aspx所述。有趣的是,它的IsSynchronized方法(正确地)返回false。
ConcurrentQueue <T>是一个完整的底层重写,它创建要枚举的队列副本,并使用诸如Interlocked.CompareExchange()和Thread.SpinWait()之类的高级无锁技术。
这个答案的其余部分在与旧的Synchronize()和SyncRoot成员的消亡以及从API角度来看它们为什么不能很好工作的范围内仍然相关。
根据Zooba的评论,BCL团队认为太多的开发人员误解了Synchronize(在较小程度上是SyncRoot)的目的。
几年前,Brian Grunkemeyer在BCL团队博客中对此进行了描述:
http://blogs.msdn.com/bclteam/archive/2005/03/15/396399.aspx
关键问题是要获得正确的锁粒度,一些开发人员会天真地在"同步"集合上使用多个属性或者方法,并认为他们的代码是线程安全的。 Brian以Queue为例
if (queue.Count > 0) { object obj = null; try { obj = queue.Dequeue();
在调用Dequeue之前,开发人员不会意识到Count可以被另一个线程更改。
强迫开发人员在整个操作过程中使用显式的lock语句意味着防止这种错误的安全感。
正如Brian所提到的,移除SyncRoot的部分原因是,它主要是为了支持同步而引入的,而且还因为在大多数情况下,大多数情况下可以更好地选择锁定对象,例如Queue实例本身或者
private static object lockObjForQueueOperations = new object();
在拥有Queue实例的类上...
后一种方法通常最安全,因为它避免了一些其他常见的陷阱:
- 永不锁定(此)
- 不要锁定(字符串)或者锁定(typeof(A))
就像他们说的那样,穿线很难,而且使穿线看起来容易很危险。