• [.NET] : Thread Safe Collection


    前言 :

    最近为了多执行绪程序分享数据集合,搞的焦头烂额。
    主要的问题点卡在,
    当有一条执行绪使用 foreach列举数据集合的时候,另外一条执行绪去变更数据集合。
    这时候会发生Exception,通知说在列举的同时数据集合被变更。

    当下最先想到的解决方案是,使用lock在读写数据集合的时候做锁定。
    这样的确可以解决问题,
    但是因为不论读写都先lock,这样会降低程序执行的效能。
    并且这样的写法,要求使用数据集合的程序代码必须要记得做lock动作,不然会发生错误。

    上网搜寻到这篇「再谈程序多任务(III)─执行绪安全与数据集合」,刚好解决了我遇到的问题点。:D

    整篇文章主要的思路就是,
    使用 ReaderWriterLockSlim,建立IEnumerable、IEnumerator接口封装lock动作。
    在读取、列举数据集合的时候,做只读的Lock。
    在写入数据集合的时候,做写入的Lock并且不允许只读的的Lock。

    依照这样的方式建立起来的数据集合,
    在使用外观上因为是建立IEnumerable、IEnumerator接口封装lock动作,使用数据集合的程序代码可以不用注意lock动作。
    并且读写两种lock分离,可以在大量取数据,少量写数据的情景下,达成最大的效率。

    本篇文章依照「再谈程序多任务(III)─执行绪安全与数据集合」的思路,整理出可扩展的Thread safe Collection。
    留个纪录,也希望能帮助到有同样问题的开发人员。 :D

    程序代码 :

    ThreadSafeReaderWriterLock :
    首先建立 ThreadSafeReaderWriterLock用来完善 ReaderWriterLock的Dipose功能。
    参考数据 : 「再谈程序多任务(I)──升级版的数据锁定和等待机制

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    
    namespace CLK.Threading
    {
        public class ThreadSafeReaderWriterLock : IDisposable
        {
            // Fields
            private readonly ReaderWriterLockSlim _readerWriterLock = new ReaderWriterLockSlim();
    
            private readonly CountdownEvent _countdownEvent = new CountdownEvent(1);
    
    
            // Constructor
            public ThreadSafeReaderWriterLock() 
            { 
    
            }
    
            public void Dispose()
            {
                _countdownEvent.Signal();
                _countdownEvent.Wait();
                _countdownEvent.Dispose();
                _readerWriterLock.Dispose();
            }
            
    
            // Methods
            public void EnterReadLock()
            {
                _countdownEvent.AddCount();
                _readerWriterLock.EnterReadLock();
            }
    
            public void ExitReadLock()
            {
                _readerWriterLock.ExitReadLock();
                _countdownEvent.Signal();
            }
    
            public void EnterWriteLock()
            {
                _countdownEvent.AddCount();
                _readerWriterLock.EnterWriteLock();
            }
    
            public void ExitWriteLock()
            {
                _readerWriterLock.ExitWriteLock();
                _countdownEvent.Signal();
            }
        }
    }
    

    ThreadSafeEnumerator<T> :
    接着建立 ThreadSafeEnumerator<T>,用以处理使用foeach做列举的lock动作。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using CLK.Threading;
    
    namespace CLK.Collections.Concurrent
    {
        public sealed class ThreadSafeEnumerator<T> : IEnumerator<T>, IDisposable
        {
            // Fields         
            private readonly ThreadSafeReaderWriterLock _readerWriterLock = null;
    
            private readonly IEnumerator<T> _component = null;        
    
    
            private readonly object _syncRoot = new object();
    
            private bool _disposed = false;
    
    
            // Constructor
            public ThreadSafeEnumerator(Func getEnumeratorDelegate, ThreadSafeReaderWriterLock readerWriterLock)
            {
                #region Require
    
                if (getEnumeratorDelegate == null) throw new ArgumentNullException();
                if (readerWriterLock == null) throw new ArgumentNullException();
    
                #endregion
    
                // ReaderWriterLock
                _readerWriterLock = readerWriterLock;
                _readerWriterLock.EnterReadLock();
    
                // Component
                _component = getEnumeratorDelegate();
            }
    
            public void Dispose()
            {
                // Require
                lock (_syncRoot)
                {
                    if (_disposed == true) return;
                    _disposed = true;
                }
    
                // Component
                _component.Dispose();
    
                // ReaderWriterLock            
                _readerWriterLock.ExitReadLock();
            }
    
    
            // Properties
            public T Current
            {
                get { return _component.Current; }
            }
    
            object System.Collections.IEnumerator.Current
            {
                get { return this.Current; }
            }
    
    
            // Methods
            public bool MoveNext()
            {
                return _component.MoveNext();
            }
    
            public void Reset()
            {
                _component.Reset();
            }
        }
    }
    

    ThreadSafeEnumerable<T> :
    再来建立 ThreadSafeEnumerable<T>,来处理读写数据 lock动作。并且他也是后续扩展数据集合的基础对象。

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using CLK.Threading;
    
    namespace CLK.Collections.Concurrent
    {
        public class ThreadSafeEnumerable<T> : IEnumerable<T>, IDisposable
        {
            // Fields         
            private readonly ThreadSafeReaderWriterLock _readerWriterLock = null;
    
            private readonly IEnumerable<T> _component = null;       
    
    
            private readonly object _syncRoot = new object();
    
            private bool _disposed = false;
    
    
            // Constructor
            protected ThreadSafeEnumerable(IEnumerable<T> component)
            {
                #region Require
               
                if (component == null) throw new ArgumentNullException();
    
                #endregion
    
                // ReaderWriterLock
                _readerWriterLock = new ThreadSafeReaderWriterLock();
    
                // Component
                _component = component;            
            }
    
            public void Dispose()
            {
                // Require
                lock (_syncRoot)
                {
                    if (_disposed == true) return;
                    _disposed = true;
                }
    
                // ReaderWriterLock            
                _readerWriterLock.Dispose();
    
                // Component
                if (_component is IDisposable)
                {
                    ((IDisposable)_component).Dispose();
                }
            }
    
    
            // Methods
            protected void EnterReadLock()
            {
                _readerWriterLock.EnterReadLock();
            }
    
            protected void ExitReadLock()
            {
                _readerWriterLock.ExitReadLock();
            }
    
            protected void EnterWriteLock()
            {
                _readerWriterLock.EnterWriteLock();
            }
    
            protected void ExitWriteLock()
            {
                _readerWriterLock.ExitWriteLock();
            }
    
    
            public IEnumerator<T> GetEnumerator()
            {
                return new ThreadSafeEnumerator<T>(_component.GetEnumerator, _readerWriterLock);
            }
    
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
        }
    }
    

    ThreadSafeCollection<T> :
    这是第一个扩展的数据集合,是以ThreadSafeEnumerable<T>为基础,并且实作ICollection<T>的接口功能。
    特别要注意的是,读写数据集合式采用不同的Lock动作。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using System.Threading;
    
    namespace CLK.Collections.Concurrent
    {
        public class ThreadSafeCollection<T> : ThreadSafeEnumerable<T>, ICollection<T>
        {
            // Fields         
            private readonly ICollection<T> _component = null;
    
    
            // Constructor
            public ThreadSafeCollection() : this(new List<T>()) { }
    
            protected ThreadSafeCollection(ICollection<T> component) : base(component)
            {
                #region Require
               
                if (component == null) throw new ArgumentNullException();
    
                #endregion
    
                // Component
                _component = component;            
            }
    
            
            // Properties
            public int Count
            {
                get
                {
                    try
                    {
                        this.EnterReadLock();
                        return _component.Count;
                    }
                    finally
                    {
                        this.ExitReadLock();
                    }
                }
            }
    
            public bool IsReadOnly
            {
                get
                {
                    try
                    {
                        this.EnterReadLock();
                        return _component.IsReadOnly;
                    }
                    finally
                    {
                        this.ExitReadLock();
                    }
                }
            }
    
    
            // Methods
            public void Add(T item)
            {
                try
                {
                    this.EnterWriteLock();
                    _component.Add(item);
                }
                finally
                {
                    this.ExitWriteLock();
                }
            }
    
            public bool Remove(T item)
            {
                try
                {
                    this.EnterWriteLock();
                    return _component.Remove(item);
                }
                finally
                {
                    this.ExitWriteLock();
                }
            }
    
            public void Clear()
            {
                try
                {
                    this.EnterWriteLock();
                    _component.Clear();
                }
                finally
                {
                    this.ExitWriteLock();
                }
            }       
    
    
            public bool Contains(T item)
            {
                try
                {
                    this.EnterReadLock();
                    return _component.Contains(item);
                }
                finally
                {
                    this.ExitReadLock();
                }
            }
    
            public void CopyTo(T[] array, int arrayIndex)
            {
                try
                {
                    this.EnterReadLock();
                    _component.CopyTo(array, arrayIndex);
                }
                finally
                {
                    this.ExitReadLock();
                }
            }        
        }
    }
    
    

    ThreadSafeList<T> :
    最后再扩展一个数据集合ThreadSafeList<T> ,是以ThreadSafeCollection<T>为基础,并且实作IList<T>的接口功能。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace CLK.Collections.Concurrent
    {
        public class ThreadSafeList<T> : ThreadSafeCollection<T>, IList<T>
        {
            // Fields         
            private readonly IList<T> _component = null;
    
    
            // Constructor
            public ThreadSafeList() : this(new List<T>()) { }
    
            protected ThreadSafeList(IList<T> component)
                : base(component)
            {
                #region Require
    
                if (component == null) throw new ArgumentNullException();
    
                #endregion
    
                // Component
                _component = component;
            }
    
    
            // Properties
            public T this[int index]
            {
                get
                {
                    try
                    {
                        this.EnterReadLock();
                        return _component[index];
                    }
                    finally
                    {
                        this.ExitReadLock();
                    }
                }
                set
                {
                    try
                    {
                        this.EnterWriteLock();
                        _component[index] = value;
                    }
                    finally
                    {
                        this.ExitWriteLock();
                    }
                }
            }
    
    
            // Methods
            public void Insert(int index, T item)
            {
                try
                {
                    this.EnterWriteLock();
                    _component.Insert(index, item);
                }
                finally
                {
                    this.ExitWriteLock();
                }
            }
    
            public void RemoveAt(int index)
            {
                try
                {
                    this.EnterWriteLock();
                    _component.RemoveAt(index);
                }
                finally
                {
                    this.ExitWriteLock();
                }
            }
    
    
            public int IndexOf(T item)
            {
                try
                {
                    this.EnterReadLock();
                    return _component.IndexOf(item);
                }
                finally
                {
                    this.ExitReadLock();
                }
            }
        }
    }
    
    

    后记 :

    依照上面扩展数据集合的方式,可以扩展出例如 IDictionary等等的各种Thread Safe Collection。 :D

  • 相关阅读:
    把旧系统迁移到.Net Core 2.0 日记 (18) --JWT 认证(Json Web Token)
    把旧系统迁移到.Net Core 2.0 日记 (17) --多租户和SoftDelete
    swagger访问api, TypeError: Failed to fetch
    nop 4.1 Widget 探究- 视图组件
    Nop 4.1版本已经迁移到.net core2.1版本
    link标签和css引入方式
    script标签
    MIME 类型
    bae64编码
    chrome调试技巧和插件介绍
  • 原文地址:https://www.cnblogs.com/clark159/p/2284081.html
Copyright © 2020-2023  润新知