Потокобезопасный GetEnumerator

Имеют ли Mutex и lock одинаковый эффект в пределах одного процесса?

Если я хочу создать потокобезопасную коллекцию с использованием внутри списка, будет ли такой вызов безопасен (за время пока элементы коллекции будет перечисляться в цикле, сама коллекция может много раз измениться):

public IEnumerator GetEnumerator() {
    lock (SyncObj) return InnerList.GetEnumerator();
}

Может нужно сделать так:

public IEnumerator GetEnumerator() {
    lock (SyncObj) return new List<object>(InnerList).GetEnumerator();
}

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

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

Имеют ли Mutex и lock одинаковый эффект в пределах одного процесса?

Эффект да, скорость - нет. Самый быстрый из двух будет lock.

Еще быстрее будет Reader/Writer lock, например на базе ReaderWriterLockSlim, так как он позволит хотя-бы безопасно читать коллекцию в многопоточке нескольким потокам сразу (пример реализации).

Если я хочу создать потокобезопасную коллекцию с использованием внутри списка, будет ли такой вызов безопасен

Первый вариант безопасен не будет, так как само перечисление будет происходить за пределами лока, а значит ваш SyncObj будет свободен для взятия другим локом. Но я бы в качестве снапшота использовал массив, а не список, он более легковесный.

Второй вариант делает снапшот коллекции под локом и отдает энумератор с нее, отличный вариант в плане того, что он не лочит саму коллекцию во время перечисления.

Но есть еще вариант

public IEnumerator<T> GetEnumerator()
{
    lock (SyncObj)
    {
        foreach (T item in InnerList)
            yield return item;
    }
}

Плюс - копия коллекции не будет создана, коллекция не будет изменена во время перечисления. Минус - коллекция будет заблокирована на время перечисления.

Вам решать, что из этого лучше сработает.

→ Ссылка