我注意到您可以调用 Queue.Synchronize 来获取线程安全的队列对象,但同样的方法在 Queue<T> 上不可用。有谁知道为什么?似乎有点奇怪。
4 回答
更新- 在 .NET 4 中,现在有ConcurrentQueue<T>
System.Collections.Concurrent,如此处所述 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 可能被另一个线程更改。
强制开发人员在整个操作周围使用显式锁定语句意味着防止这种错误的安全感。
正如 Brian 所提到的,SyncRoot 的删除部分是因为它主要是为了支持 Synchronized 而引入的,但也因为在许多情况下有更好的锁定对象选择——大多数时候,要么是 Queue 实例本身,要么是
private static object lockObjForQueueOperations = new object();
在拥有队列实例的类上......
后一种方法通常是最安全的,因为它避免了一些其他常见的陷阱:
正如他们所说,线程很难,让它看起来很容易可能很危险。
您可能会发现 Parallel CTP 值得一试;这是将它放在一起的人的博客条目,非常热门:
这不是一回事,但它可能会解决你更大的问题。(他们甚至使用Queue<T>
vsConcurrentQueue<T>
作为他们的例子。)
现在有一个,在 .Net 4.0 中:
ConcurrentQueue<T>
在 System.Collections.Concurrent
(我假设您的意思是 Queue<T> 用于第二个。)
我无法具体回答这个问题,除了 IsSynchronized 和 SyncRoot 属性(但不是 Synchronise() 显式)是从 ICollection 接口继承的。没有一个通用集合使用它,并且 ICollection<T> 接口不包括 SyncRoot。
至于为什么不包括在内,我只能推测它们没有按照图书馆设计者的预期方式使用,或者只是没有被充分利用以证明将它们保留在较新的馆藏中是合理的。