Потокобезопасные списки C#

У меня вопрос, есть ли какие нибудь потокобезопасные списки? Я понимаю что можно использовать lock, но интересует способ без использования блокировки коллекции.

Просто в теории допустим, что есть около 20 потоков которые работают с 1 коллекцией и как можно добавлять и изымать оттуда "информацию".


Ответы (1 шт):

Автор решения: aepot

Так как комментариев накидали много, а прямого дубликата данного вопроса я не нашел, оформлю ответом.

Потокобезопасные коллекции

Пространство имён System.Collections.Concurrent (документация) содержит все стандартные потокобезопасные коллекции. Их поведение и методы отличаются от обычных аналогов, поэтому стоит изучить, как с ними правильно работать. Половина потокобезопасных коллекций строится на интерфейсе IProducerConsumerCollection<T>, он то и добавляет что-то общее между этими коллекциями. Остальные имеют более специфическое назначение.

Почитать про потокобезопасные коллекции можно здесь. Можно посмотреть даже видео.

ConcurrentList<T>

Так же стоит отметить, что в .NET отсутствует коллекция ConcurrentList<T>, есть подробный ответ на этот вопрос.

Вот пример реализации самого простого потокобезопасного списка на базе ReaderWriterLockSlim, который я когда-то написал для другого ответа. Можно считать это болванкой для своего собственного решения.

ConcurrentList.cs

public class ConcurrentList<T> : IEnumerable<T>, ICollection<T>
{
    private readonly List<T> _list;
    private readonly ReaderWriterLockSlim _lock;

    public int Count 
    {
        get
        {
            try
            {
                _lock.EnterReadLock();
                return _list.Count;
            }
            finally
            {
                _lock.ExitReadLock();
            }
        }
    }

    public bool IsReadOnly => false;

    public ConcurrentList() : this(null) { }

    public ConcurrentList(IEnumerable<T> items)
    {
        _list = items is null ? new List<T>() : new List<T>(items);
        _lock = new ReaderWriterLockSlim();
    }

    public void Add(T item)
    {
        try
        {
            _lock.EnterWriteLock();
            _list.Add(item);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public void Clear()
    {
        try
        {
            _lock.EnterWriteLock();
            _list.Clear();
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    public bool Contains(T item)
    {
        try
        {
            _lock.EnterReadLock();
            return _list.Contains(item);
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        try
        {
            _lock.EnterReadLock();
            _list.CopyTo(array, arrayIndex);
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public bool Remove(T item)
    {
        try
        {
            _lock.EnterWriteLock();
            return _list.Remove(item);
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }

    private IEnumerable<T> Enumerate()
    {
        try
        {
            _lock.EnterReadLock();
            foreach (T item in _list)
                yield return item;
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }

    public IEnumerator<T> GetEnumerator()
        => Enumerate().GetEnumerator();

    IEnumerator IEnumerable.GetEnumerator()
        => Enumerate().GetEnumerator();
}

Неизменяемые коллекции

Так же существуют неизменяемые коллекции System.Collections.Immutable. Они тоже являются потокобезопасными, так как данные в них нельзя изменить, а следовательно к ним можно получать безопасный доступ из нескольких потоков. Подробнее почитать можно здесь.

→ Ссылка