C# 无ConcurrentList<;T>;在.NET4.0中?
在.NET4.0中看到新的C# 无ConcurrentList<;T>;在.NET4.0中?,c#,.net,parallel-processing,task-parallel-library,C#,.net,Parallel Processing,Task Parallel Library,在.NET4.0中看到新的System.Collections.Concurrent命名空间,我非常激动,非常好!我看过ConcurrentDictionary,ConcurrentQueue,ConcurrentStack,ConcurrentBag和BlockingCollection 一个似乎神秘地缺失的东西是ConcurrentList。我必须自己写(或从网上下载:)吗 我是否遗漏了一些明显的信息?您使用ConcurrentList做什么 线程世界中的随机访问容器的概念并不像看上去那么有
System.Collections.Concurrent
命名空间,我非常激动,非常好!我看过ConcurrentDictionary
,ConcurrentQueue
,ConcurrentStack
,ConcurrentBag
和BlockingCollection
一个似乎神秘地缺失的东西是ConcurrentList
。我必须自己写(或从网上下载:)吗
我是否遗漏了一些明显的信息?您使用ConcurrentList做什么 线程世界中的随机访问容器的概念并不像看上去那么有用。声明
if (i < MyConcurrentList.Count)
x = MyConcurrentList[i];
if(i
作为一个整体,它仍然不是线程安全的
不要创建ConcurrentList,而是尝试用现有的内容构建解决方案。最常见的类是ConcurrentBag,尤其是BlockingCollection。
ConcurrentList
(作为一个可调整大小的数组,而不是链表)不容易使用非阻塞操作编写。它的API不能很好地转换为“并发”版本。System.Collections.Generic.List对于多个读卡器来说已经是线程安全的。试图使它对多个编写器线程安全是没有意义的。(由于亨克和斯蒂芬已经提到的原因)I(也:)。我的实现遇到了一些问题,在这里我将不讨论这些问题。让我告诉你,更重要的是,我学到了什么
首先,您不可能得到一个无锁和线程安全的IList
的完整实现。特别是,随机插入和删除是行不通的,除非你也忘记了O(1)随机访问(也就是说,除非你“作弊”,只是使用某种链表,让索引变得糟糕)
我认为值得的是一个线程安全的、有限的IList子集:特别是,它允许添加,并通过索引提供随机只读访问(但不允许插入,删除,等等,也不允许随机写访问)
这就是我们的目标。但是当我在多线程场景中测试它的性能时,我发现简单地同步添加到列表
更快。基本上,加入列表
已经是闪电般的快了;所涉及的计算步骤的复杂性很小(增加索引并分配给数组中的一个元素;实际上就是这样)。您需要大量的并发写入才能看到任何类型的锁争用;即使如此,每次写入的平均性能仍将超过ConcurrentList
中更昂贵但无锁的实现
在相对罕见的情况下,列表的内部数组需要自行调整大小,您需要支付少量的成本。因此,我最终得出结论,这是一个利基场景,其中仅添加ConcurrentList
集合类型是有意义的:当您希望保证在每个调用中添加元素的开销较低时(因此,与摊销性能目标相反)
它根本没有你想象的那么有用。一些人提到了一些有用的观点(以及我的一些想法):
- 它可能看起来像疯狂的无法随机存取器(索引器),但对我来说,它似乎很好。您只需想想,在多线程集合上有许多方法可能会失败,比如Indexer和Delete。您还可以为写访问器定义失败(回退)操作,如“失败”或简单地“在末尾添加”李>
- 并不是因为它是一个多线程集合,所以它总是在多线程上下文中使用。或者它也可以只被一个作者和一个读者使用
- 以安全的方式使用indexer的另一种方法是使用集合的根(若已公开)将操作包装到集合的锁中
- 对许多人来说,让根锁可见违背了“良好实践”。我不是100%确定这一点,因为如果它是隐藏的,你会给用户带来很大的灵活性。我们必须始终记住,编程多线程并不适合任何人。我们不能防止每一种错误的使用
- 微软将不得不做一些工作并定义一些新的标准来引入多线程集合的正确使用。首先,IEnumerator不应该有moveNext,而应该有一个GetNext,返回true或false,并获取类型为T的out参数(这样迭代就不会再阻塞)。此外,微软已经在foreach内部使用了“using”,但有时直接使用IEnumerator,而不使用“using”(集合视图中的一个bug,可能在更多的地方)——微软建议使用IEnumerator包装。此错误消除了安全迭代器的良好潜力。。。迭代器,用于在构造函数中锁定集合并在其Dispose方法上解锁-用于阻塞foreach方法李>
这不是答案。这只是一些不适合特定地方的评论
。。。我的结论是,微软必须对“foreach”进行一些深入的修改,以使多线程集合更易于使用。它还必须遵循IEnumerator的使用规则。在此之前,我们可以很容易地编写一个多线程列表,该列表将使用阻塞迭代器,但不会跟随“IList”。相反,您必须定义自己的“IListPersonnal”接口,该接口在“插入”、“删除”和随机访问器(索引器)上可能会毫无例外地失败。但是,如果它不是标准的,谁会想使用它呢?尽管已经提供了很多很好的答案,但有时我只是想要一个线程安全的IList。没有什么先进或新奇的东西。在许多情况下,性能很重要,但有时这并不重要。是的,如果没有“TryGetValue”等方法,总会有挑战,但大多数情况下,我只是想要一些
public class ConcurrentList<T> : IList<T>, IDisposable
{
#region Fields
private readonly List<T> _list;
private readonly ReaderWriterLockSlim _lock;
#endregion
#region Constructors
public ConcurrentList()
{
this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
this._list = new List<T>();
}
public ConcurrentList(int capacity)
{
this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
this._list = new List<T>(capacity);
}
public ConcurrentList(IEnumerable<T> items)
{
this._lock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
this._list = new List<T>(items);
}
#endregion
#region Methods
public void Add(T item)
{
try
{
this._lock.EnterWriteLock();
this._list.Add(item);
}
finally
{
this._lock.ExitWriteLock();
}
}
public void Insert(int index, T item)
{
try
{
this._lock.EnterWriteLock();
this._list.Insert(index, item);
}
finally
{
this._lock.ExitWriteLock();
}
}
public bool Remove(T item)
{
try
{
this._lock.EnterWriteLock();
return this._list.Remove(item);
}
finally
{
this._lock.ExitWriteLock();
}
}
public void RemoveAt(int index)
{
try
{
this._lock.EnterWriteLock();
this._list.RemoveAt(index);
}
finally
{
this._lock.ExitWriteLock();
}
}
public int IndexOf(T item)
{
try
{
this._lock.EnterReadLock();
return this._list.IndexOf(item);
}
finally
{
this._lock.ExitReadLock();
}
}
public void Clear()
{
try
{
this._lock.EnterWriteLock();
this._list.Clear();
}
finally
{
this._lock.ExitWriteLock();
}
}
public bool Contains(T item)
{
try
{
this._lock.EnterReadLock();
return this._list.Contains(item);
}
finally
{
this._lock.ExitReadLock();
}
}
public void CopyTo(T[] array, int arrayIndex)
{
try
{
this._lock.EnterReadLock();
this._list.CopyTo(array, arrayIndex);
}
finally
{
this._lock.ExitReadLock();
}
}
public IEnumerator<T> GetEnumerator()
{
return new ConcurrentEnumerator<T>(this._list, this._lock);
}
IEnumerator IEnumerable.GetEnumerator()
{
return new ConcurrentEnumerator<T>(this._list, this._lock);
}
~ConcurrentList()
{
this.Dispose(false);
}
public void Dispose()
{
this.Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
GC.SuppressFinalize(this);
this._lock.Dispose();
}
#endregion
#region Properties
public T this[int index]
{
get
{
try
{
this._lock.EnterReadLock();
return this._list[index];
}
finally
{
this._lock.ExitReadLock();
}
}
set
{
try
{
this._lock.EnterWriteLock();
this._list[index] = value;
}
finally
{
this._lock.ExitWriteLock();
}
}
}
public int Count
{
get
{
try
{
this._lock.EnterReadLock();
return this._list.Count;
}
finally
{
this._lock.ExitReadLock();
}
}
}
public bool IsReadOnly
{
get { return false; }
}
#endregion
}
public class ConcurrentEnumerator<T> : IEnumerator<T>
{
#region Fields
private readonly IEnumerator<T> _inner;
private readonly ReaderWriterLockSlim _lock;
#endregion
#region Constructor
public ConcurrentEnumerator(IEnumerable<T> inner, ReaderWriterLockSlim @lock)
{
this._lock = @lock;
this._lock.EnterReadLock();
this._inner = inner.GetEnumerator();
}
#endregion
#region Methods
public bool MoveNext()
{
return _inner.MoveNext();
}
public void Reset()
{
_inner.Reset();
}
public void Dispose()
{
this._lock.ExitReadLock();
}
#endregion
#region Properties
public T Current
{
get { return _inner.Current; }
}
object IEnumerator.Current
{
get { return _inner.Current; }
}
#endregion
}
public class ConcurrentList<T> : IList<T>, IDisposable
{
private ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
private int _count = 0;
public int Count
{
get
{
_lock.EnterReadLock();
try
{
return _count;
}
finally
{
_lock.ExitReadLock();
}
}
}
public int InternalArrayLength
{
get
{
_lock.EnterReadLock();
try
{
return _arr.Length;
}
finally
{
_lock.ExitReadLock();
}
}
}
private T[] _arr;
public ConcurrentList(int initialCapacity)
{
_arr = new T[initialCapacity];
}
public ConcurrentList():this(4)
{ }
public ConcurrentList(IEnumerable<T> items)
{
_arr = items.ToArray();
_count = _arr.Length;
}
public void Add(T item)
{
_lock.EnterWriteLock();
try
{
var newCount = _count + 1;
EnsureCapacity(newCount);
_arr[_count] = item;
_count = newCount;
}
finally
{
_lock.ExitWriteLock();
}
}
public void AddRange(IEnumerable<T> items)
{
if (items == null)
throw new ArgumentNullException("items");
_lock.EnterWriteLock();
try
{
var arr = items as T[] ?? items.ToArray();
var newCount = _count + arr.Length;
EnsureCapacity(newCount);
Array.Copy(arr, 0, _arr, _count, arr.Length);
_count = newCount;
}
finally
{
_lock.ExitWriteLock();
}
}
private void EnsureCapacity(int capacity)
{
if (_arr.Length >= capacity)
return;
int doubled;
checked
{
try
{
doubled = _arr.Length * 2;
}
catch (OverflowException)
{
doubled = int.MaxValue;
}
}
var newLength = Math.Max(doubled, capacity);
Array.Resize(ref _arr, newLength);
}
public bool Remove(T item)
{
_lock.EnterUpgradeableReadLock();
try
{
var i = IndexOfInternal(item);
if (i == -1)
return false;
_lock.EnterWriteLock();
try
{
RemoveAtInternal(i);
return true;
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
public IEnumerator<T> GetEnumerator()
{
_lock.EnterReadLock();
try
{
for (int i = 0; i < _count; i++)
// deadlocking potential mitigated by lock recursion enforcement
yield return _arr[i];
}
finally
{
_lock.ExitReadLock();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public int IndexOf(T item)
{
_lock.EnterReadLock();
try
{
return IndexOfInternal(item);
}
finally
{
_lock.ExitReadLock();
}
}
private int IndexOfInternal(T item)
{
return Array.FindIndex(_arr, 0, _count, x => x.Equals(item));
}
public void Insert(int index, T item)
{
_lock.EnterUpgradeableReadLock();
try
{
if (index > _count)
throw new ArgumentOutOfRangeException("index");
_lock.EnterWriteLock();
try
{
var newCount = _count + 1;
EnsureCapacity(newCount);
// shift everything right by one, starting at index
Array.Copy(_arr, index, _arr, index + 1, _count - index);
// insert
_arr[index] = item;
_count = newCount;
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
public void RemoveAt(int index)
{
_lock.EnterUpgradeableReadLock();
try
{
if (index >= _count)
throw new ArgumentOutOfRangeException("index");
_lock.EnterWriteLock();
try
{
RemoveAtInternal(index);
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
private void RemoveAtInternal(int index)
{
Array.Copy(_arr, index + 1, _arr, index, _count - index-1);
_count--;
// release last element
Array.Clear(_arr, _count, 1);
}
public void Clear()
{
_lock.EnterWriteLock();
try
{
Array.Clear(_arr, 0, _count);
_count = 0;
}
finally
{
_lock.ExitWriteLock();
}
}
public bool Contains(T item)
{
_lock.EnterReadLock();
try
{
return IndexOfInternal(item) != -1;
}
finally
{
_lock.ExitReadLock();
}
}
public void CopyTo(T[] array, int arrayIndex)
{
_lock.EnterReadLock();
try
{
if(_count > array.Length - arrayIndex)
throw new ArgumentException("Destination array was not long enough.");
Array.Copy(_arr, 0, array, arrayIndex, _count);
}
finally
{
_lock.ExitReadLock();
}
}
public bool IsReadOnly
{
get { return false; }
}
public T this[int index]
{
get
{
_lock.EnterReadLock();
try
{
if (index >= _count)
throw new ArgumentOutOfRangeException("index");
return _arr[index];
}
finally
{
_lock.ExitReadLock();
}
}
set
{
_lock.EnterUpgradeableReadLock();
try
{
if (index >= _count)
throw new ArgumentOutOfRangeException("index");
_lock.EnterWriteLock();
try
{
_arr[index] = value;
}
finally
{
_lock.ExitWriteLock();
}
}
finally
{
_lock.ExitUpgradeableReadLock();
}
}
}
public void DoSync(Action<ConcurrentList<T>> action)
{
GetSync(l =>
{
action(l);
return 0;
});
}
public TResult GetSync<TResult>(Func<ConcurrentList<T>,TResult> func)
{
_lock.EnterWriteLock();
try
{
return func(this);
}
finally
{
_lock.ExitWriteLock();
}
}
public void Dispose()
{
_lock.Dispose();
}
}
static class CopyOnWriteSwapper
{
public static void Swap<T>(ref T obj, Func<T, T> cloner, Action<T> op)
where T : class
{
while (true)
{
var objBefore = Volatile.Read(ref obj);
var newObj = cloner(objBefore);
op(newObj);
if (Interlocked.CompareExchange(ref obj, newObj, objBefore) == objBefore)
return;
}
}
}
CopyOnWriteSwapper.Swap(ref _myList,
orig => new List<string>(orig),
clone => clone.Add("asdf"));
int catIndex = list.IndexOf("cat");
list.Insert(catIndex, "dog");
public class CopyAndWriteList<T>
{
public static List<T> Clear(List<T> list)
{
var a = new List<T>(list);
a.Clear();
return a;
}
public static List<T> Add(List<T> list, T item)
{
var a = new List<T>(list);
a.Add(item);
return a;
}
public static List<T> RemoveAt(List<T> list, int index)
{
var a = new List<T>(list);
a.RemoveAt(index);
return a;
}
public static List<T> Remove(List<T> list, T item)
{
var a = new List<T>(list);
a.Remove(item);
return a;
}
}