如何创建线程安全的通用列表?

我有一个通用列表如下

public static readonly List Customers = new List(); 

我正在使用以下方法:

 .Add .Find .FirstOrDefault 

最后2个是LINQ扩展。

我需要使这个线程安全的能够运行容器类的多个实例。

怎么实现呢?

如果这些是您在List上使用的唯一函数,那么最简单的方法是编写一个快速包装器,将访问与lock同步

 class MyList { private List _list = new List(); private object _sync = new object(); public void Add(T value) { lock (_sync) { _list.Add(value); } } public bool Find(Predicate predicate) { lock (_sync) { return _list.Find(predicate); } } public T FirstOrDefault() { lock (_sync) { return _list.FirstOrDefault(); } } } 

我强烈推荐新类型+私有锁对象的方法。 对于inheritance你的代码的下一个人来说,实际意图是什么让它变得更加明显。

另请注意,.Net 4.0引入了一组新的集合,专门用于从多个线程中使用。 如果其中一个满足您的需求,我强烈建议您使用它自己滚动。

  • ConcurrentStack
  • ConcurrentQueue

如果您使用的是.NET Framework 4或更高版本,则可以使用线程安全集合 。

您可以用ConcurrentBag替换List ConcurrentBag

 namespace Playground.Sandbox { using System.Collections.Concurrent; using System.Threading.Tasks; public static class Program { public static void Main() { var items = new[] { "Foo", "Bar", "Baz" }; var bag = new ConcurrentBag(); Parallel.ForEach(items, bag.Add); } } } 

为了扩展@ JaradPar的答案,这里是一个完整的实现,带有一些额外的function,如摘要中所述

  ///  /// a thread-safe list with support for: /// 1) negative indexes (read from end). "myList[-1]" gets the last value /// 2) modification while enumerating: enumerates a copy of the collection. ///  ///  public class ConcurrentList : IList { private object _lock = new object(); private List _storage = new List(); ///  /// support for negative indexes (read from end). "myList[-1]" gets the last value ///  ///  ///  public TValue this[int index] { get { lock (_lock) { if (index < 0) { index = this.Count - index; } return _storage[index]; } } set { lock (_lock) { if (index < 0) { index = this.Count - index; } _storage[index] = value; } } } public void Sort() { lock (_lock) { _storage.Sort(); } } public int Count { get { return _storage.Count; } } bool ICollection.IsReadOnly { get { return ((IList)_storage).IsReadOnly; } } public void Add(TValue item) { lock (_lock) { _storage.Add(item); } } public void Clear() { lock (_lock) { _storage.Clear(); } } public bool Contains(TValue item) { lock (_lock) { return _storage.Contains(item); } } public void CopyTo(TValue[] array, int arrayIndex) { lock (_lock) { _storage.CopyTo(array, arrayIndex); } } public int IndexOf(TValue item) { lock (_lock) { return _storage.IndexOf(item); } } public void Insert(int index, TValue item) { lock (_lock) { _storage.Insert(index, item); } } public bool Remove(TValue item) { lock (_lock) { return _storage.Remove(item); } } public void RemoveAt(int index) { lock (_lock) { _storage.RemoveAt(index); } } public IEnumerator GetEnumerator() { lock (_lock) { lock (_lock) { return (IEnumerator)_storage.ToArray().GetEnumerator(); } } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } 

您将需要在修改或迭代集合的每个位置使用锁。

或者使用其中一个新的线程安全数据结构,如ConcurrentBag 。

在操作集合时使用lock关键字,即:您的添加/查找:

 lock(Customers) { Customers.Add(new Customer()); } 

只有在任何私有对象上使用锁定,才能使您的Action可访问

请参阅:线程安全通用队列类

http://www.codeproject.com/Articles/38908/Thread-Safe-Generic-Queue-Class

好的,所以我不得不完全重写我的答案。 经过2天的测试后,我不得不说JasonS的代码有一些缺陷,我猜是因为有了枚举器。 当一个线程使用foreach,而另一个线程更改列表时,它会抛出exception。

所以我找到了这个答案 ,它在过去48小时不间断地对我很有用,我想在我的应用程序中创建了超过100k个线程,并使用了该列表。

我改变的唯一一件事 – 我已经进入了try-finally部分之外的锁定。 请在此处阅读可能的例外情况。 此外,如果您将阅读MSDN,他们有相同的方法。

但是,正如下面链接中提到的,List不能100%线程安全,这可能就是为什么c#中没有默认的ConcurentList实现。

切勿将ConcurrangBag用于订购数据。 请改用Array