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

标签 c# .net multithreading c#-4.0 task-parallel-library

完全编辑早期版本,以下实现可以是“线程安全列表”实现。我只需要知道它是否真的可以线程安全,就性能而言我仍然会遇到问题。当前版本使用ReaderWriterLockSlim,我还有另一个使用Lock的实现,执行相同的工作

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

/// <summary>
/// Thread safe version of the List using ReaderWriterLockSlim 
/// </summary>
/// <typeparam name="T"></typeparam>
public class ThreadSafeListWithRWLock<T> : IList<T>
{
    // Internal private list which would be accessed in a thread safe manner
    private List<T> internalList;

    // ReaderWriterLockSlim object to take care of thread safe acess between multiple readers and writers
    private readonly ReaderWriterLockSlim rwLockList;

    /// <summary>
    /// Public constructor with variable initialization code
    /// </summary>
    public ThreadSafeListWithRWLock()
    {
        internalList = new List<T>();

        rwLockList = new ReaderWriterLockSlim();
    }

    /// <summary>
    /// Get the Enumerator to the Thread safe list
    /// </summary>
    /// <returns></returns>
    public IEnumerator<T> GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    /// <summary>
    /// System.Collections.IEnumerable.GetEnumerator implementation to get the IEnumerator type
    /// </summary>
    /// <returns></returns>
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return Clone().GetEnumerator();
    }

    /// <summary>
    /// Clone method to create an in memory copy of the Thread safe list
    /// </summary>
    /// <returns></returns>
    public List<T> Clone()
    {
        List<T> clonedList = new List<T>();

        rwLockList.EnterReadLock();

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

        rwLockList.ExitReadLock();

        return (clonedList);
    }

    /// <summary>
    /// Add an item to Thread safe list
    /// </summary>
    /// <param name="item"></param>
    public void Add(T item)
    {
        rwLockList.EnterWriteLock();

        internalList.Add(item);

        rwLockList.ExitWriteLock();
    }

    /// <summary>
    /// Remove an item from Thread safe list
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public bool Remove(T item)
    {
        bool isRemoved;

        rwLockList.EnterWriteLock();

        isRemoved = internalList.Remove(item);

        rwLockList.ExitWriteLock();

        return (isRemoved);
    }

    /// <summary>
    /// Clear all elements of Thread safe list
    /// </summary>
    public void Clear()
    {
        rwLockList.EnterWriteLock();

        internalList.Clear();

        rwLockList.ExitWriteLock();
    }

    /// <summary>
    /// Contains an item in the Thread safe list
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public bool Contains(T item)
    {
        bool containsItem;

        rwLockList.EnterReadLock();

        containsItem = internalList.Contains(item);

        rwLockList.ExitReadLock();

        return (containsItem);
    }

    /// <summary>
    /// Copy elements of the Thread safe list to a compatible array from specified index in the aray
    /// </summary>
    /// <param name="array"></param>
    /// <param name="arrayIndex"></param>
    public void CopyTo(T[] array, int arrayIndex)
    {
        rwLockList.EnterReadLock();

        internalList.CopyTo(array,arrayIndex);

        rwLockList.ExitReadLock();
    }

    /// <summary>
    /// Count elements in a Thread safe list
    /// </summary>
    public int Count
    {
        get
        {
            int count;

            rwLockList.EnterReadLock();

            count = internalList.Count;

            rwLockList.ExitReadLock();

            return (count);
        }
    }

    /// <summary>
    /// Check whether Thread safe list is read only
    /// </summary>
    public bool IsReadOnly
    {
        get { return false; }
    }

    /// <summary>
    /// Index of an item in the Thread safe list
    /// </summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public int IndexOf(T item)
    {
        int itemIndex;

        rwLockList.EnterReadLock();

        itemIndex = internalList.IndexOf(item);

        rwLockList.ExitReadLock();

        return (itemIndex);
    }

    /// <summary>
    /// Insert an item at a specified index in a Thread safe list
    /// </summary>
    /// <param name="index"></param>
    /// <param name="item"></param>
    public void Insert(int index, T item)
    {
      rwLockList.EnterWriteLock();

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

      rwLockList.ExitWriteLock();
    }

    /// <summary>
    /// Remove an item at a specified index in Thread safe list
    /// </summary>
    /// <param name="index"></param>
    public void RemoveAt(int index)
    {
       rwLockList.EnterWriteLock();

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

       rwLockList.ExitWriteLock();
    }

    /// <summary>
    /// Indexer for the Thread safe list
    /// </summary>
    /// <param name="index"></param>
    /// <returns></returns>
    public T this[int index] 
    {
        get
        {
            T returnItem = default(T);

           rwLockList.EnterReadLock();

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

           rwLockList.ExitReadLock();

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

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

            rwLockList.ExitWriteLock();
        }
    }
}

最佳答案

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

但是我本人是一个绩效密集型行业,因此有时会成为瓶颈。 lock的主要缺点是可能进行上下文切换,相对而言,这在挂钟时间和CPU周期上都非常昂贵。

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

我要强调的是,在几乎所有情况下,这都是矫kill过正的,除非您肯定需要,并且您了解缺点,否则我什至不会考虑走这条路。几个明显的例子是读者获取时间点快照并浪费了内存来创建副本。

ImmutableList中的Microsoft.Bcl.Immutable也值得一看。这是完全线程安全的。

关于c# - 使用Reader Writer Lock创建线程安全列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25681321/

相关文章:

c# - 当 ThreadStart 例程返回时,线程是否会自动停止?

c++ - GDB 在非线程应用程序中显示 1 个线程

c# - 正确路由到Umbraco ASP.NET/IApplicationEventHander与ApplicationEventHandler与RouteConfig.cs,RenderMvcController等中的 Controller 的正确方法

c# - Entity Framework 核心,更新数据库不考虑属性

c# - 如何确保只有一个View Model Base类实例?

.net - 调试 IIS 进程的 CPU 使用率过高

.net - 如何将程序集 BAML 转换为 XAML?

multithreading - 当 child 被杀死时,防止线程 subprocess.popen 终止我的主脚本?

c# - 如何将文本框中的每个字符串转换为字符串?

c# - Microsoft.Office.Interop.Outlook.Items.Restrict - 无法正常工作