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.

Comments

Popular posts from this blog

Tutorial: Using Google Cloud Storage from C# and .NET

Late 2008 Macbook only giving 1.5 gb/s speed with 6 gb/s Intel SSD?

Enable Visual Studio to use more than 2GB of memory