A WriteThreadSafeCollection implementation
/// <summary>
/// In this class modifying the list is thread safe. To enumerate through the list, always lock the SyncRoot property, otherwise it will not be thread safe. This is the recommended way of making a collection thread safe.
/// </summary>
/// <typeparam name="T">The type of the entity for which the collection is created.</typeparam>
[Serializable]
[DataContract]
public class WriteThreadSafeCollection<T> : Collection<T>
{
private object _syncLock = new object();
public WriteThreadSafeCollection() : base()
{
}
public WriteSynchronizedCollection(IList<T> sourceList) : base()
{
this.AddRange(sourceList);
}
protected override void InsertItem(int index, T item)
{
lock (_syncLock)
{
base.InsertItem(index, item);
}
}
protected override void ClearItems()
{
lock (_syncLock)
{
base.ClearItems();
}
}
protected override void RemoveItem(int index)
{
lock (_syncLock)
{
base.RemoveItem(index);
}
}
public void AddRange(IEnumerable<T> collection)
{
if (collection == null)
{
throw (new ArgumentNullException("collection", "collection is null."));
}
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
//Already lock safe
this.Add(enumerator.Current);
}
}
}
public void RemoveRange(int index, int count)
{
if ((index < 0) || (count < 0))
{
throw (new ArgumentOutOfRangeException("index, count", "index is less than 0 or count is less than 0."));
}
if ((this.Count - index) < count)
{
throw (new ArgumentException("index and count do not denote a valid range of elements in the list.", "index, count"));
}
IList<T> itemsInRange = null;
lock (_syncLock)
{
itemsInRange = (from p in base.Items
select p).Skip(index).Take(count).ToList();
}
if (itemsInRange != null && itemsInRange.Count > 0)
{
for (int i = 0; i < itemsInRange.Count; i++)
{
//Already lock safe
base.Remove(itemsInRange[i]);
}
}
}
protected override void SetItem(int index, T item)
{
lock (_syncLock)
{
base.SetItem(index, item);
}
}
public object SyncRoot
{
get
{
return _syncLock;
}
}
}
Just because I am using the syncLock object here does not mean that, it should be the way we do it everywhere. Think of this as an identifier to the thing which really needs to be locked. Here it is access to the reading or writing of the underlying list.
If multiple things need to be locked within a class, there should be multiple lock variables.
/// In this class modifying the list is thread safe. To enumerate through the list, always lock the SyncRoot property, otherwise it will not be thread safe. This is the recommended way of making a collection thread safe.
/// </summary>
/// <typeparam name="T">The type of the entity for which the collection is created.</typeparam>
[Serializable]
[DataContract]
public class WriteThreadSafeCollection<T> : Collection<T>
{
private object _syncLock = new object();
public WriteThreadSafeCollection() : base()
{
}
public WriteSynchronizedCollection(IList<T> sourceList) : base()
{
this.AddRange(sourceList);
}
protected override void InsertItem(int index, T item)
{
lock (_syncLock)
{
base.InsertItem(index, item);
}
}
protected override void ClearItems()
{
lock (_syncLock)
{
base.ClearItems();
}
}
protected override void RemoveItem(int index)
{
lock (_syncLock)
{
base.RemoveItem(index);
}
}
public void AddRange(IEnumerable<T> collection)
{
if (collection == null)
{
throw (new ArgumentNullException("collection", "collection is null."));
}
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
//Already lock safe
this.Add(enumerator.Current);
}
}
}
public void RemoveRange(int index, int count)
{
if ((index < 0) || (count < 0))
{
throw (new ArgumentOutOfRangeException("index, count", "index is less than 0 or count is less than 0."));
}
if ((this.Count - index) < count)
{
throw (new ArgumentException("index and count do not denote a valid range of elements in the list.", "index, count"));
}
IList<T> itemsInRange = null;
lock (_syncLock)
{
itemsInRange = (from p in base.Items
select p).Skip(index).Take(count).ToList();
}
if (itemsInRange != null && itemsInRange.Count > 0)
{
for (int i = 0; i < itemsInRange.Count; i++)
{
//Already lock safe
base.Remove(itemsInRange[i]);
}
}
}
protected override void SetItem(int index, T item)
{
lock (_syncLock)
{
base.SetItem(index, item);
}
}
public object SyncRoot
{
get
{
return _syncLock;
}
}
}
Just because I am using the syncLock object here does not mean that, it should be the way we do it everywhere. Think of this as an identifier to the thing which really needs to be locked. Here it is access to the reading or writing of the underlying list.
If multiple things need to be locked within a class, there should be multiple lock variables.
Comments
Post a Comment