List <T>的线程安全版本 - 实现

Mri*_*boj 0 .net c# parallel-processing multithreading task-parallel-library

经过Stack Overflow上的大量帖子后,我想我能够提出一个线程安全版本的List,这肯定不符合Concurrent集合的水平,因为它使用了ReaderWriterLockSlim,但在我的理解中,它与简单的锁定版本相比,它可以按预期工作并具有更好的性能.您可能认为的任何内容都可以在当前的实现中得到改进.它仍然没有实现List的所有功能,因为我刚刚处理了IList

免责声明 - 我从Stack Overflow中得到了一些想法,所以它肯定包含来自不同帖子的点点滴滴

修改 - 修改代码以处理某些情况,这些情景在上次通信中发布,如:

if(list.count > 0)
  return list[0]
Run Code Online (Sandbox Code Playgroud)

作为偏离主题,没有理由将此标记为暂停

线程安全实现

using System.Collections.Generic;
using System.Threading;


/// <summary>
/// Thread safe version of the List using 
/// </summary>
/// <typeparam name="T"></typeparam>
public class ThreadSafeListWithRWLock<T> : IList<T>
{
    private List<T> internalList;

    private readonly ReaderWriterLockSlim rwLockList;

    public ThreadSafeListWithRWLock()
    {
        internalList = new List<T>();

        rwLockList = new ReaderWriterLockSlim();
    }

    // Other Elements of IList implementation

    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    public List<T> Clone()
    {
        List<T> clonedList = new List<T>();

        rwLockList.EnterReadLock();

        internalList.ForEach(element => { clonedList.Add(element); });

        rwLockList.ExitReadLock();

        return (clonedList);
    }

    public void Add(T item)
    {
        rwLockList.EnterWriteLock();

        internalList.Add(item);

        rwLockList.ExitWriteLock();
    }

    public bool Remove(T item)
    {
        bool isRemoved;

        rwLockList.EnterWriteLock();

        isRemoved = internalList.Remove(item);

        rwLockList.ExitWriteLock();

        return (isRemoved);
    }

    public void Clear()
    {
        rwLockList.EnterWriteLock();

        internalList.Clear();

        rwLockList.ExitWriteLock();
    }

    public bool Contains(T item)
    {
        bool containsItem;

        rwLockList.EnterReadLock();

        containsItem = internalList.Contains(item);

        rwLockList.ExitReadLock();

        return (containsItem);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        rwLockList.EnterReadLock();

        internalList.CopyTo(array,arrayIndex);

        rwLockList.ExitReadLock();
    }

    public int Count
    {
        get
        {
            int count;

            rwLockList.EnterReadLock();

            count = internalList.Count;

            rwLockList.ExitReadLock();

            return (count);
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public int IndexOf(T item)
    {
        int itemIndex;

        rwLockList.EnterReadLock();

        itemIndex = internalList.IndexOf(item);

        rwLockList.ExitReadLock();

        return (itemIndex);
    }

    public void Insert(int index, T item)
    {
      rwLockList.EnterWriteLock();

      if (index <= internalList.Count - 1)
        internalList.Insert(index,item);

      rwLockList.ExitWriteLock();
    }

    public void RemoveAt(int index)
    {
       rwLockList.EnterWriteLock();

       if (index <= internalList.Count - 1)
        internalList.RemoveAt(index);

       rwLockList.ExitWriteLock();
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T this[int index] 
    {
        get
        {
            T returnItem = default(T);

           rwLockList.EnterReadLock();

           if (index <= internalList.Count - 1)
               returnItem = internalList[index];              

           rwLockList.ExitReadLock();

            return (returnItem);
        }
        set
        {
            rwLockList.EnterWriteLock();

            if (index <= internalList.Count - 1)
                internalList[index] = value;

            rwLockList.ExitWriteLock();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Oli*_*ier 8

这种实现有点漏洞,因为即使每个原子操作(例如每个方法调用)都是线程安全的,总体上也容易出错.

为了说明,请考虑这种代码:

if(!myThreadSafeList.Contains(item))
       myThreadSafeList.Add(item);
Run Code Online (Sandbox Code Playgroud)

这两个操作是线程安全的,但总体来说不是.

你可以使用.Net conccurent集合,正如trailmax在评论中所建议的那样.

另一种方法是使用 Nuget上提供的Microsoft immutable集合.

这些是线程安全的,而且,无锁!

Ps:如果您打算使用不可变集合,请了解一些有关 比较和交换循环的信息,尽管...使用Interlocked.CompareExchange方法在C#中实现.

  • 实际上,和(提供另一个例子)代码如`if(list.Count> 0)返回列表[0];`仍然是错误的并且可能抛出异常. (3认同)