SynchronizedCollection和 SynchronizedCollection之间有什么区别?和其他并发集合?

发布于 2024-10-12 00:21:28 字数 293 浏览 11 评论 0原文

SynchronizedCollectionSystem.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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

誰認得朕 2024-10-19 00:21:28

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 a List<T> where every access is wrapped in a lock 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 the IList<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 the System.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 the SynchronizedCollection<T> class.

This article on MSDN is also worth a read: When to Use a Thread-Safe Collection

固执像三岁 2024-10-19 00:21:28

SynchronizedCollection是同步的 列表。这是一个可以在一秒钟内设计出来的概念,可以是 在大约一小时内完全实施。只需将 List 的每个方法包装在 lock (this) 中,就完成了。现在您有了一个线程安全的集合,它可以满足多线程应用程序的所有需求。但事实并非如此。

一旦您尝试用 SynchronizedCollection 做任何重要的事情,它的缺点就会变得明显。特别是当您尝试将集合的两个或多个方法组合起来进行概念上的单一操作时。然后您意识到该操作不是原子的,并且如果不诉诸显式同步就无法使其成为原子的(锁定 SyncRoot 集合的属性),这破坏了集合的整体目的。一些示例:

  1. 确保集合包含唯一元素:if (!collection.Contains(x)) collection.Add(x);。这段代码不能保证任何事情。 ContainsAdd 之间固有的竞争条件允许出现重复。
  2. 确保集合最多包含 N 个元素:if (collection.Count < N) collection.Add(x);CountAdd 之间的竞争条件允许集合中存在超过 N 个元素。
  3. "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(TryDequeueGetOrAddAddOrUpdate 等),并且还具有旨在最大限度地减少大量使用情况下的争用。在内部,它们采用无锁、低锁和粒度锁技术。学习如何使用这些集合需要一些研究。它们不是非并发对应项的直接替代品。

注意:SynchronizedCollection 的枚举不同步。使用 GetEnumerator 获取枚举器是同步的,但使用枚举器则不是。因此,如果一个线程执行 foreach (var item in collection),而另一个线程以任何方式改变集合(AddRemove 等),程序的行为是未定义的。枚举 SynchronizedCollection 的安全方法是获取集合的快照,然后枚举该快照。获取快照并不简单,因为它涉及两个方法调用(Count getter 和 CopyTo),因此需要显式同步。注意 LINQ ToArray< /a> 运算符,它本身不是线程安全的。下面是 SynchronizedCollection 类的安全 ToArraySafe 扩展方法:

/// <summary>Copies the elements of the collection to a new array.</summary>
public static T[] ToArraySafe<T>(this SynchronizedCollection<T> source)
{
    ArgumentNullException.ThrowIfNull(source);
    lock (source.SyncRoot)
    {
        T[] array = new T[source.Count];
        source.CopyTo(array, 0);
        return array;
    }
}

The SynchronizedCollection<T> is a synchronized List<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 a List<T> inside a lock (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 the SyncRoot property of the collection), which undermines the whole purpose of the collection. Some examples:

  1. Ensure that the collection contains unique elements: if (!collection.Contains(x)) collection.Add(x);. This code ensures nothing. The inherent race condition between Contains and Add allows duplicates to occur.
  2. Ensure that the collection contains at most N elements: if (collection.Count < N) collection.Add(x);. The race condition between Count and Add allows more than N elements in the collection.
  3. Replace "Foo" with "Bar": int index = collection.IndexOf("Foo"); if (index >= 0) collection[index] = "Bar";. When a thread reads the index, its value is immediately stale. Another thread might change the collection in a way that the index 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 the ConcurrentDictionary<K,V>, are highly sophisticated components. They are orders of magnitude more sophisticated than the clumsy SynchronizedCollection<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 with GetEnumerator is synchronized, but using the enumerator is not. So if one thread does a foreach (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 a SynchronizedCollection<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 (the Count getter and the CopyTo), so explicit synchronization is required. Beware of the LINQ ToArray operator, it's not thread-safe by itself. Below is a safe ToArraySafe extension method for the SynchronizedCollection<T> class:

/// <summary>Copies the elements of the collection to a new array.</summary>
public static T[] ToArraySafe<T>(this SynchronizedCollection<T> source)
{
    ArgumentNullException.ThrowIfNull(source);
    lock (source.SyncRoot)
    {
        T[] array = new T[source.Count];
        source.CopyTo(array, 0);
        return array;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文