SynchronizedCollection和 SynchronizedCollection之间有什么区别?和其他并发集合?
SynchronizedCollection
和 System.Collections.Concurrent
命名空间中的并发集合有何不同,除了并发集合是一个命名空间而 SynchronizedCollection
是一个命名空间之外。 T>
是一个类?
SynchronizedCollection
和并发集合中的所有类都提供线程安全集合。我如何决定何时使用其中一种而不是另一种,为什么?
How does SynchronizedCollection<T>
and the concurrent collections in the System.Collections.Concurrent
namespace differ from each other, apart from Concurrent Collections being a namespace and SynchronizedCollection<T>
being a class?
SynchronizedCollection<T>
and all of the classes in Concurrent Collections provide thread-safe collections. How do I decide when to use one over the other, and why?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
SynchronizedCollection
类首先在.NET 2.0中引入,以提供线程安全的集合类。它通过锁定来完成此操作,因此您实际上有一个List
,其中每个访问都包含在lock
语句中。System.Collections.Concurrent
命名空间要更新得多。它直到 .NET 4.0 才被引入,它包括一组经过大幅改进且更加多样化的选择。这些类不再使用锁来提供线程安全性,这意味着它们应该在多个线程同时访问其数据的情况下更好地扩展。然而,这些选项中明显缺少实现 IList因此,如果您的目标是 .NET Framework 4.0 版本,则应尽可能使用 System.Collections.Concurrent 命名空间提供的集合之一。就像在
System.Collections.Generic
命名空间,您需要选择一个其功能和特性最适合您的特定需求的命名空间。如果您的目标是旧版本的 .NET Framework 或需要一个实现
IList
接口的集合类,则必须选择SynchronizedCollection< /代码> 类。
MSDN 上的这篇文章也值得一读:何时使用线程安全集合< /a>
The
SynchronizedCollection<T>
class was introduced first in .NET 2.0 to provide a thread-safe collection class. It does this via locking so that you essentially have aList<T>
where every access is wrapped in alock
statement.The
System.Collections.Concurrent
namespace is much newer. It wasn't introduced until .NET 4.0 and it includes a substantially improved and more diverse set of choices. These classes no longer use locks to provide thread safety, which means they should scale better in a situation where multiple threads are accessing their data simultaneously. However, a class implementing theIList<T>
interface is notably absent among these options.So, if you're targeting version 4.0 of the .NET Framework, you should use one of the collections provided by the
System.Collections.Concurrent
namespace whenever possible. Just as with choosing between the various types of collections provided in theSystem.Collections.Generic
namespace, you'll need to choose the one whose features and characteristics best fit your specific needs.If you're targeting an older version of the .NET Framework or need a collection class that implements the
IList<T>
interface, you'll have to opt for theSynchronizedCollection<T>
class.This article on MSDN is also worth a read: When to Use a Thread-Safe Collection
SynchronizedCollection
是同步的列表
。这是一个可以在一秒钟内设计出来的概念,可以是 在大约一小时内完全实施。只需将List
的每个方法包装在lock (this)
中,就完成了。现在您有了一个线程安全的集合,它可以满足多线程应用程序的所有需求。但事实并非如此。一旦您尝试用
SynchronizedCollection
做任何重要的事情,它的缺点就会变得明显。特别是当您尝试将集合的两个或多个方法组合起来进行概念上的单一操作时。然后您意识到该操作不是原子的,并且如果不诉诸显式同步就无法使其成为原子的(锁定SyncRoot
集合的属性),这破坏了集合的整体目的。一些示例:if (!collection.Contains(x)) collection.Add(x);
。这段代码不能保证任何事情。Contains
和Add
之间固有的竞争条件允许出现重复。if (collection.Count < N) collection.Add(x);
。Count
和Add
之间的竞争条件允许集合中存在超过 N 个元素。"Foo"
替换为"Bar"
:int index = collection.IndexOf("Foo"); if (index >= 0) 集合[index] = "Bar";
.当线程读取index
时,它的值立即失效。另一个线程可能会以index
指向某个其他元素的方式更改集合,或者它超出范围。此时您意识到多线程比您最初想象的要求更高。在现有集合的 API 周围添加同步层并不能解决问题。您需要一个专为多线程使用而设计的集合,并且具有反映此设计的 API。这就是引入并发集合 在 .NET Framework 4.0 中。
并发集合,例如
ConcurrentQueue< ;T>
和ConcurrentDictionary
是高度复杂的组件。它们比笨拙的SynchronizedCollection
复杂几个数量级。它们配备了非常适合多线程环境的特殊原子 API(TryDequeue
、GetOrAdd
、AddOrUpdate
等),并且还具有旨在最大限度地减少大量使用情况下的争用。在内部,它们采用无锁、低锁和粒度锁技术。学习如何使用这些集合需要一些研究。它们不是非并发对应项的直接替代品。注意:
SynchronizedCollection
的枚举不同步。使用GetEnumerator
获取枚举器是同步的,但使用枚举器则不是。因此,如果一个线程执行foreach (var item in collection)
,而另一个线程以任何方式改变集合(Add
、Remove
等),程序的行为是未定义的。枚举SynchronizedCollection
的安全方法是获取集合的快照,然后枚举该快照。获取快照并不简单,因为它涉及两个方法调用(Count
getter 和CopyTo
),因此需要显式同步。注意 LINQToArray
< /a> 运算符,它本身不是线程安全的。下面是SynchronizedCollection
类的安全ToArraySafe
扩展方法:The
SynchronizedCollection<T>
is a synchronizedList<T>
. It's a concept that can be devised in a second, and can be implemented fully in about one hour. Just wrap each method of aList<T>
inside alock (this)
, and you are done. Now you have a thread-safe collection, that can cover all the needs of a multithreaded application. Except that it doesn't.The shortcomings of the
SynchronizedCollection<T>
become apparent as soon as you try to do anything non-trivial with it. Specifically as soon as you try to combine two or more methods of the collection for a conceptually singular operation. Then you realize that the operation is not atomic, and cannot be made atomic without resorting to explicit synchronization (locking on theSyncRoot
property of the collection), which undermines the whole purpose of the collection. Some examples:if (!collection.Contains(x)) collection.Add(x);
. This code ensures nothing. The inherent race condition betweenContains
andAdd
allows duplicates to occur.if (collection.Count < N) collection.Add(x);
. The race condition betweenCount
andAdd
allows more than N elements in the collection."Foo"
with"Bar"
:int index = collection.IndexOf("Foo"); if (index >= 0) collection[index] = "Bar";
. When a thread reads theindex
, its value is immediately stale. Another thread might change the collection in a way that theindex
points to some other element, or it's out of range.At this point you realize that multithreading is more demanding than what you originally thought. Adding a layer of synchronization around the API of an existing collection doesn't cut it. You need a collection that is designed from the ground up for multithreaded usage, and has an API that reflects this design. This was the motivation for the introduction of the concurrent collections in .NET Framework 4.0.
The concurrent collections, for example the
ConcurrentQueue<T>
and theConcurrentDictionary<K,V>
, are highly sophisticated components. They are orders of magnitude more sophisticated than the clumsySynchronizedCollection<T>
. They are equipped with special atomic APIs that are well suited for multithreaded environments (TryDequeue
,GetOrAdd
,AddOrUpdate
etc), and also with implementations that aim at minimizing the contention under heavy usage. Internally they employ lock-free, low-lock and granular-lock techniques. Learning how to use these collections requires some study. They are not direct drop-in replacements of their non-concurrent counterparts.Caution: the enumeration of a
SynchronizedCollection<T>
is not synchronized. Getting an enumerator withGetEnumerator
is synchronized, but using the enumerator is not. So if one thread does aforeach (var item in collection)
while another thread mutates the collection in any way (Add
,Remove
etc), the behavior of the program is undefined. The safe way to enumerate aSynchronizedCollection<T>
is to get a snapshot of the collection, and then enumerate the snapshot. Getting a snapshot is not trivial, because it involves two method calls (theCount
getter and theCopyTo
), so explicit synchronization is required. Beware of the LINQToArray
operator, it's not thread-safe by itself. Below is a safeToArraySafe
extension method for theSynchronizedCollection<T>
class: