List.Add()线程安全

我知道一般List不是线程安全的,但是如果线程从不在列表中执行任何其他操作(比如遍历它),那么简单地将项添加到列表中有什么问题吗?

例:

List<object> list = new List<object>(); Parallel.ForEach(transactions, tran => { list.Add(new object()); }); 

幕后发生了很多事情,包括重新分配缓冲区和复制元素。 该代码将导致危险。 非常简单,当添加到列表时,没有primefaces操作,至less“Length”属性需要更新,并且项目需要放在正确的位置,(如果有单独的variables)索引需要要被更新。 多个线程可以互相践踏。 如果需要增长,那么还有更多的事情要做。 如果有什么东西正在写入列表中,则其他任何内容都不应该读取或写入。

在.NET 4.0中,我们有并发的集合,它们是简单的线程安全的并且不需要锁。

你目前的做法是不是线程安全的 – 我build议完全避免这一点 – 因为你基本上做一个数据转换 PLINQ可能是一个更好的方法(我知道这是一个简化的例子,但最终你将每个事务投影到另一个“状态“对象)。

 List<object> list = transactions.AsParallel() .Select( tran => new object()) .ToList(); 

问这不是一个不合理的事情。 有些情况下,可能导致线程安全问题与其他方法结合使用的方法是安全的,如果它们是唯一的方法调用。

但是,这显然不是一个例子,当你考虑在reflection器中显示的代码:

 public void Add(T item) { if (this._size == this._items.Length) { this.EnsureCapacity(this._size + 1); } this._items[this._size++] = item; this._version++; } 

即使EnsureCapacity本身是线程安全的(而且当然不是),上面的代码显然不会是线程安全的,考虑到同时调用增量操作符导致写错的可能性。

无论是locking,使用ConcurrentList,还是使用一个无锁队列作为multithreading写入的地方,以及在它们完成工作之后直接或通过填充列表来读取它(我假设然后单线程读取多个同时写入是你的模式在这里,从你的问题来判断,否则我不能看到如何Add是唯一的方法调用可以是任何使用的条件)。

这会造成问题,因为List是build立在一个数组上,而且不是线程安全的,你可能会得到索引超出界限的exception,或者某些值覆盖其他值,这取决于线程的位置。 基本上,不要这样做。

有多个潜在的问题…只是不。 如果您需要线程安全集合,请使用locking或System.Collections.Concurrent集合之一。

如果线程从不在列表中执行任何其他操作,将项添加到列表中是否有任何问题?

简短的回答:是的。

长答案:运行下面的程序。

 using System; using System.Collections.Generic; using System.Linq; using System.Threading; class Program { readonly List<int> l = new List<int>(); const int amount = 1000; int toFinish = amount; readonly AutoResetEvent are = new AutoResetEvent(false); static void Main() { new Program().Run(); } void Run() { for (int i = 0; i < amount; i++) new Thread(AddTol).Start(i); are.WaitOne(); if (l.Count != amount || l.Distinct().Count() != amount || l.Min() < 0 || l.Max() >= amount) throw new Exception("omg corrupted data"); Console.WriteLine("All good"); Console.ReadKey(); } void AddTol(object o) { // uncomment to fix // lock (l) l.Add((int)o); int i = Interlocked.Decrement(ref toFinish); if (i == 0) are.Set(); } } 

正如其他人所说,你可以使用来自System.Collections.Concurrent命名空间的并发集合。 如果你可以使用其中之一,这是首选。

但是,如果你真的想要一个刚刚同步的列表,你可以看看System.Collections.Generic中的SynchronizedCollection<T>类。

请注意,您必须包含System.ServiceModel程序集,这也是我不喜欢它的原因。 但有时我用它。

如果你想使用来自多个线程的List.add而不关心sorting,那么你可能不需要List的索引能力,而应该使用一些可用的并发集合。

如果你忽略这个build议,只做add ,你可以使线程安全,但以不可预知的顺序如下:

 private Object someListLock = new Object(); // only once ... lock (someListLock) { someList.Add(item); } 

如果你接受这个不可预测的sorting,那么你可能就像前面提到的那样不需要一个能够像someList[i]someList[i]进行索引的集合。

即使在不同线程上添加元素也不是线程安全的。

在C#4.0中有并发集合(请参阅http://jiezhu0815.blogspot.com/2010/08/c-40-feature-1-concurrent-collections.html )。