使用Reader Writer Lock创建线程安全列表

完全编辑早期版本,以下实现可以是线程安全列表实现。 我只需要知道它是否真的是线程安全的,我知道性能明智仍然存在问题。 目前版本正在使用ReaderWriterLockSlim,我有另一个使用Lock的实现,做同样的工作

使用System.Collections.Generic; 使用System.Threading;

///  /// Thread safe version of the List using ReaderWriterLockSlim ///  ///  public class ThreadSafeListWithRWLock : IList { // Internal private list which would be accessed in a thread safe manner private List internalList; // ReaderWriterLockSlim object to take care of thread safe acess between multiple readers and writers private readonly ReaderWriterLockSlim rwLockList; ///  /// Public constructor with variable initialization code ///  public ThreadSafeListWithRWLock() { internalList = new List(); rwLockList = new ReaderWriterLockSlim(); } ///  /// Get the Enumerator to the Thread safe list ///  ///  public IEnumerator GetEnumerator() { return Clone().GetEnumerator(); } ///  /// System.Collections.IEnumerable.GetEnumerator implementation to get the IEnumerator type ///  ///  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return Clone().GetEnumerator(); } ///  /// Clone method to create an in memory copy of the Thread safe list ///  ///  public List Clone() { List clonedList = new List(); rwLockList.EnterReadLock(); internalList.ForEach(element => { clonedList.Add(element); }); rwLockList.ExitReadLock(); return (clonedList); } ///  /// Add an item to Thread safe list ///  ///  public void Add(T item) { rwLockList.EnterWriteLock(); internalList.Add(item); rwLockList.ExitWriteLock(); } ///  /// Remove an item from Thread safe list ///  ///  ///  public bool Remove(T item) { bool isRemoved; rwLockList.EnterWriteLock(); isRemoved = internalList.Remove(item); rwLockList.ExitWriteLock(); return (isRemoved); } ///  /// Clear all elements of Thread safe list ///  public void Clear() { rwLockList.EnterWriteLock(); internalList.Clear(); rwLockList.ExitWriteLock(); } ///  /// Contains an item in the Thread safe list ///  ///  ///  public bool Contains(T item) { bool containsItem; rwLockList.EnterReadLock(); containsItem = internalList.Contains(item); rwLockList.ExitReadLock(); return (containsItem); } ///  /// Copy elements of the Thread safe list to a compatible array from specified index in the aray ///  ///  ///  public void CopyTo(T[] array, int arrayIndex) { rwLockList.EnterReadLock(); internalList.CopyTo(array,arrayIndex); rwLockList.ExitReadLock(); } ///  /// Count elements in a Thread safe list ///  public int Count { get { int count; rwLockList.EnterReadLock(); count = internalList.Count; rwLockList.ExitReadLock(); return (count); } } ///  /// Check whether Thread safe list is read only ///  public bool IsReadOnly { get { return false; } } ///  /// Index of an item in the Thread safe list ///  ///  ///  public int IndexOf(T item) { int itemIndex; rwLockList.EnterReadLock(); itemIndex = internalList.IndexOf(item); rwLockList.ExitReadLock(); return (itemIndex); } ///  /// Insert an item at a specified index in a Thread safe list ///  ///  ///  public void Insert(int index, T item) { rwLockList.EnterWriteLock(); if (index = 0) internalList.Insert(index,item); rwLockList.ExitWriteLock(); } ///  /// Remove an item at a specified index in Thread safe list ///  ///  public void RemoveAt(int index) { rwLockList.EnterWriteLock(); if (index = 0) internalList.RemoveAt(index); rwLockList.ExitWriteLock(); } ///  /// Indexer for the Thread safe list ///  ///  ///  public T this[int index] { get { T returnItem = default(T); rwLockList.EnterReadLock(); if (index = 0) returnItem = internalList[index]; rwLockList.ExitReadLock(); return (returnItem); } set { rwLockList.EnterWriteLock(); if (index = 0) internalList[index] = value; rwLockList.ExitWriteLock(); } } } 

实现封装线程安全的自定义List很少值得付出努力。 每当您访问List时,您最好只使用lock

但是,在我自己处于性能密集型行业的情况下,有些情况会成为瓶颈。 lock的主要缺点是上下文切换的可能性,相对而言,在挂钟时间和CPU周期中都非常昂贵。

解决这个问题的最佳方法是使用不变性。 让所有读者访问不可变列表,编写者使用Interlocked操作“更新”它以用新实例替换它。 这是一种无锁设计,使读取无需同步并且无锁定写入(消除了上下文切换)。

我会强调,在几乎所有情况下,这都是矫枉过正的,我甚至不会考虑沿着这条路走下去,除非你是积极的,你需要了解并且你理解这些缺点。 其中一些显而易见的是读者获取时间点快照并浪费内存创建副本。

来自Microsoft.Bcl.Immutable的 ImmutableList也值得一看。 它完全是线程安全的。

这不是线程安全的。

在返回枚举数之后, GetEnumerator()方法不会保留任何锁,因此任何线程都可以自由地使用返回的枚举器而不进行任何锁定,以防止它们这样做。

通常,尝试创建线程安全列表类型非常困难。

有关讨论,请参阅此StackOverflow线程: .Net 4.0中没有ConcurrentList

如果您尝试使用某种读取器/写入器锁定,而不是简单的读取和写入锁定方案,则并发读取可能会大大超过您的写入次数。 在这种情况下,Zer0建议的写时复制方法可能是合适的。

在回答相关问题时,我发布了一个通用实用程序函数,它有助于将对任何数据结构的任何修改转换为线程安全且高度并发的操作。

 static class CopyOnWriteSwapper { public static void Swap(ref T obj, Func cloner, Action 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(orig), clone => clone.Add("asdf")); 

关于你可以用它做什么的更多细节,以及一些警告可以在原始答案中找到。