为什么.NET中没有通用的同步队列?

时间:2020-03-06 14:28:37  来源:igfitidea点击:

我注意到我们可以调用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))

就像他们说的那样,穿线很难,而且使穿线看起来容易很危险。