C# 如何创建线程安全的泛型列表?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9995266/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to Create a Thread-Safe Generic List?
提问by The Light
I have a Generic List as below
我有一个通用列表如下
public static readonly List<Customer> Customers = new List<Customer>();
I'm using the below methods for it:
我正在使用以下方法:
.Add
.Find
.FirstOrDefault
The last 2 are LINQ extensions.
最后 2 个是 LINQ 扩展。
I'd need to make this thread-safe to be able to run multiple instances of the container class.
我需要使这个线程安全才能运行容器类的多个实例。
How to achieve that?
如何做到这一点?
采纳答案by JaredPar
If those are the only functions you are using on List<T>then the easiest way is to write a quick wrapper that synchronizes access with a lock
如果这些是您使用的唯一功能,List<T>那么最简单的方法是编写一个快速包装器,将访问与lock
class MyList<T> {
private List<T> _list = new List<T>();
private object _sync = new object();
public void Add(T value) {
lock (_sync) {
_list.Add(value);
}
}
public bool Find(Predicate<T> predicate) {
lock (_sync) {
return _list.Find(predicate);
}
}
public T FirstOrDefault() {
lock (_sync) {
return _list.FirstOrDefault();
}
}
}
I highly recommend the approach of a new type + private lock object. It makes it much more obvious to the next guy who inherits your code what the actual intent was.
我强烈推荐新类型+私有锁对象的方法。它让下一个继承你代码的人更清楚真正的意图是什么。
Also note that .Net 4.0 introduced a new set of collections specifically aimed at being used from multiple threads. If one of these meets your needs I'd highly recommend using it over rolling your own.
另请注意,.Net 4.0 引入了一组新的集合,专门针对多线程使用。如果其中之一满足您的需求,我强烈建议您使用它而不是自己滚动。
ConcurrentStack<T>ConcurrentQueue<T>
ConcurrentStack<T>ConcurrentQueue<T>
回答by Tudor
You will need to use locks in every place where the collection gets modified or iterated over.
您将需要在集合被修改或迭代的每个地方使用锁。
Either that or use one of the new thread-safe data structures, like ConcurrentBag.
或者使用一种新的线程安全数据结构,比如ConcurrentBag。
回答by JSJ
Make your Action as accessible by one only by using lock on any private object
通过对任何私有对象使用锁定,使您的操作只能被一个人访问
Refer to : Thread Safe Generic Queue Class
参考:线程安全通用队列类
http://www.codeproject.com/Articles/38908/Thread-Safe-Generic-Queue-Class
http://www.codeproject.com/Articles/38908/Thread-Safe-Generic-Queue-Class
回答by Darren
Use the lock keyword when you manipulate the collection, ie: your Add/Find:
操作集合时使用 lock 关键字,即:您的添加/查找:
lock(Customers) {
Customers.Add(new Customer());
}
回答by Albireo
If you're using version 4 or greater of the .NET framework you can use the thread-safe collections.
如果您使用 .NET 框架的第 4 版或更高版本,则可以使用线程安全集合。
You can replace List<T>with ConcurrentBag<T>:
您可以替换List<T>为ConcurrentBag<T>:
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<string>();
Parallel.ForEach(items, bag.Add);
}
}
}
回答by JasonS
To expand on @JaradPar's answer, here is a full implementation with a few extra features, as described in the summary
为了扩展@JaradPar 的答案,这里是一个完整的实现,具有一些额外的功能,如摘要中所述
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TValue"></typeparam>
public class ConcurrentList<TValue> : IList<TValue>
{
private object _lock = new object();
private List<TValue> _storage = new List<TValue>();
/// <summary>
/// support for negative indexes (read from end). "myList[-1]" gets the last value
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
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<TValue>.IsReadOnly
{
get
{
return ((IList<TValue>)_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<TValue> GetEnumerator()
{
lock (_lock)
{
lock (_lock)
{
return (IEnumerator<TValue>)_storage.ToArray().GetEnumerator();
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
回答by Max
Ok, so I had to completely rewrite my answer. After 2 days of testing I have to say that the JasonS's code has some defects, I guess because of Enumerators. While one thread uses foreach, and the other other changes the list, it throws exceptions.
好的,所以我不得不完全重写我的答案。经过2天的测试,我不得不说JasonS的代码有一些缺陷,我猜是因为枚举器。当一个线程使用 foreach,另一个线程更改列表时,它会抛出异常。
So I found this answer, and it works for me fine the last 48 hours non-stop, I guess more than 100k threads were created in my application, and used that lists.
所以我找到了这个答案,它在过去 48 小时不间断地对我有用,我猜在我的应用程序中创建了超过 10 万个线程,并使用了该列表。
The only thing I changed - I've moved entering the locks outside the try-finally section. Read hereabout the possible exceptions. Also, if you will read MSDN, they have the same approach.
我唯一改变的事情 - 我已经进入了 try-finally 部分之外的锁。在此处阅读有关可能的例外情况。另外,如果你会阅读 MSDN,他们有相同的方法。
But, as were mentioned in link below, List can not be 100% thread safe, probably that is why there is no default ConcurentList implementation in c#.
但是,正如在下面的链接中提到的,List 不能是 100% 线程安全的,这可能就是 c# 中没有默认 ConcurentList 实现的原因。
回答by Moctar Haiz
Never use ConcurrangBag for ordered data. Use Array instead
切勿将 ConcurrangBag 用于有序数据。改用数组

