[.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
期許自己~
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。

浙公网安备 33010602011771号