Thread-safe enumeration in C#

I had a problem with the project I’m working on at work where on the base class I had a list which its child classes need to have access to in a read-only capacity but manipulation to the list itself is handled by the base class only. However, the standard C# List<T> and Enumerator<T> are not thread-safe and we started seeing problems when the list is modified by one thread whilst another is trying to loop through it.

Whilst looking for a clean solution we found this article on CodeProject:

http://www.codeproject.com/KB/cs/safe_enumerable.aspx

The article had covered much of the implementation you need, but left some gaps you need to plug yourself for a thread-safe list which I have included below along with some of the useful methods you’d find on List<T>:

Thread-safe Enumerator<T>

/// <summary>
/// A thread-safe IEnumerator implementation.
/// See: http://www.codeproject.com/KB/cs/safe_enumerable.aspx
/// </summary>
public class SafeEnumerator<T>: IEnumerator<T>
{
    // this is the (thread-unsafe)
    // enumerator of the underlying collection
    private readonly IEnumerator<T> _inner;

    // this is the object we shall lock on.
    private readonly object _lock;

    public SafeEnumerator(IEnumerator<T> inner, object @lock)
    {
        _inner = inner;
        _lock = @lock;

        // entering lock in constructor
        Monitor.Enter(_lock);
    }

    public T Current
    {
        get { return _inner.Current; }
    }

    object IEnumerator.Current
    {
        get { return Current; }
    }

    public void Dispose()
    {
        // .. and exiting lock on Dispose()
        // This will be called when foreach loop finishes
        Monitor.Exit(_lock);
    }

    /// <remarks>
    /// we just delegate actual implementation
    /// to the inner enumerator, that actually iterates
    /// over some collection
    /// </remarks>
    public bool MoveNext()
    {
        return _inner.MoveNext();
    }

    public void Reset()
    {
        _inner.Reset();
    }
}

Thread-safe List<T>

/// <summary>
/// A thread-safe IList implementation using the custom SafeEnumerator class
/// See: http://www.codeproject.com/KB/cs/safe_enumerable.aspx
/// </summary>
public class SafeList<T> : IList<T>
{
    // the (thread-unsafe) collection that actually stores everything
    private readonly List<T> _inner;

    // this is the object we shall lock on.
    private readonly object _lock = new object();

    public SafeList()
    {
        _inner = new List<T>();
    }

    public int Count
    {
        get
        {
            lock (_lock)
            {
                return _inner.Count;
            }
        }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public T this[int index]
    {
        get
        {
            lock (_lock)
            {
                return _inner[index];
            }
        }
        set
        {
            lock (_lock)
            {
                _inner[index] = value;
            }
        }
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        lock (_lock)
        {
            // instead of returning an usafe enumerator,
            // we wrap it into our thread-safe class
            return new SafeEnumerator<T>(_inner.GetEnumerator(), _lock);
        }
    }

    /// <remarks>
    /// To be actually thread-safe, our collection must be locked on all other operations
    /// </remarks>
    public void Add(T item)
    {
        lock (_lock)
        {
            _inner.Add(item);
        }
    }

    public void Clear()
    {
        lock (_lock)
        {
            _inner.Clear();
        }
    }

    public bool Contains(T item)
    {
        lock (_lock)
        {
            return _inner.Contains(item);
        }
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        lock (_lock)
        {
            _inner.CopyTo(array, arrayIndex);
        }
    }

    public bool Remove(T item)
    {
        lock (_lock)
        {
            return _inner.Remove(item);
        }
    }

    public IEnumerator GetEnumerator()
    {
        lock (_lock)
        {
            return new SafeEnumerator<T>(_inner.GetEnumerator(), _lock);
        }
    }

    public int IndexOf(T item)
    {
        lock (_lock)
        {
            return _inner.IndexOf(item);
        }
    }

    public void Insert(int index, T item)
    {
        lock (_lock)
        {
            _inner.Insert(index, item);
        }
    }

    public void RemoveAt(int index)
    {
        lock (_lock)
        {
            _inner.RemoveAt(index);
        }
    }

    public ReadOnlyCollection<T> AsReadOnly()
    {
        lock (_lock)
        {
            return new ReadOnlyCollectio<T>(this);
        }
    }

    [CheckParameters]
    public void ForEach([NotNull] Action<T> action)
    {
        lock (_lock)
        {
            foreach (var item in _inner)
            {
                action(item);
            }
        }
    }

    [CheckParameters]
    public bool Exists([NotNull] Predicate<T> match)
    {
        lock (_lock)
        {
            foreach (var item in _inner)
            {
                if (match(item))
                {
                    return true;
                }
            }
        }
        return false;
    }
}
You can see I’ve used the CheckParameters and NotNull attributes for parameter validation using PostSharp, you can easily substitute them with normal null checks.

Thread-safe IEnumerable<T>

/// <summary>
/// A thread-safe IEnumerable implementation
/// See: http://www.codeproject.com/KB/cs/safe_enumerable.aspx
/// </summary>
public class SafeEnumerable<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> _inner;
    private readonly object _lock;

    public SafeEnumerable(IEnumerable<T> inner, object @lock)
    {
        _lock = @lock;
        _inner = inner;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new SafeEnumerator<T>(_inner.GetEnumerator(), _lock);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Enjoy what you’re reading? Subscribe to my newsletter and get more content on AWS and serverless technologies delivered straight to your inbox.


Yan Cui

I’m an AWS Serverless Hero and the author of Production-Ready Serverless. I have run production workload at scale in AWS for nearly 10 years and I have been an architect or principal engineer with a variety of industries ranging from banking, e-commerce, sports streaming to mobile gaming. I currently work as an independent consultant focused on AWS and serverless.

You can contact me via Email, Twitter and LinkedIn.

Hire me.


Check out my new course, Complete Guide to AWS Step Functions.

In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. Including basic concepts, HTTP and event triggers, activities, design patterns and best practices.

Get Your Copy