Является ли List <T>.AddRange() потоком безопасным?

Могу ли я, без блокировки, безопасно вызвать List.AddRange(r) из нескольких потоков? Если нет, с какими проблемами я столкнулся бы?

Ответы

Ответ 1

Нет, его документация не говорит, что это поточно-безопасный, поэтому это не так.

Открытый статический (общий в Visual Basic) члены этого типа являются потокобезопасными. Любые члены экземпляра не являются гарантированно надежный поток.

Что касается того, что может пойти не так, подумайте о том, что делает AddRange (newItems):

  • Проверьте, достаточно ли свободного места во внутреннем массиве
  • Если нет:
    • Выделить новый массив
    • Скопировать текущие элементы в новый массив
    • Задайте поле для указания нового массива
  • Скопируйте newItems в локальный локальный массив
  • Обновите поле "count" (это используется для управления местом, где вставлен следующий элемент).

Теперь подумайте, что произойдет, если вышеперечисленное смешано с другим вызовом AddRange() или даже просто для вызова элемента.

Ответ 2

Нет, нет, но я хотел бы добавить его более эффективно, чтобы сделать myList.AddRange(...); внутри блокировки, чем несколько lock (syncLock) { myList.Add(...) };.

Какую проблему вы столкнулись бы? Когда один поток добавляет элемент, а другой перечисляет список, List<T> будет вызывать определенное исключение, потому что он выполняет некоторое внутреннее управление версиями, так как он хочет, чтобы наши бедные разработчики не сталкивались с неприятными побочными эффектами.

Также List<T> внутренне сохраняет массив, в котором он хранит свои элементы. Возможно, настройка элемента в массиве довольно атомарна, но всякий раз, когда достигается пропускная способность этого массива, будет создан новый, и элементы будут скопированы из старого. Поэтому, когда поток хочет что-то добавить, пока это копирование имеет место, вы можете себе представить, что все будет выходить из строя.

Ответ 3

До .NET Framework 4.0 коллекции .NET не являются потокобезопасными. Затем вам нужно будет заблокировать его, прежде чем вы получите доступ к нему в своем коде Collections and Synchronization (Thread Safety).

С другой стороны,.NET Framework 4.0 представляет новое пространство имен System.Collections.Concurrent, которое включает в себя мелкозернистый Thread-Safe Collections.

Наконец, если вы можете использовать .NET Framework 4.0, я настоятельно рекомендую вам сделать это за то, что вам нужно, в противном случае обязательно заблокируйте сборку каждый раз, когда вы хотите изменить или получить к ней доступ.

Кроме того, статический сбор должен быть потокобезопасным, но будьте осторожны, поскольку члены не гарантируются.

РЕДАКТИРОВАТЬ № 1

После дальнейших проверок из-за комментария Стива Таунсенда я признаю, что в .NET Framework есть три потокобезопасные коллекции, начиная с версии 3.0:

Прошу прощения, я только что узнал их выход. =)

Ответ 4

В зависимости от вашего использования SynchronizedCollection может работать.

У вас не было бы одного выстрела AddRange. Если вы используете это только для семени коллекции, вы можете сделать это, поскольку существует перегрузка конструктора IEnumerable.

Ответ 5

Нет, он не является потокобезопасным.

Тема A может вызвать AddRange в вашем списке. Он может частично перебираться по потокам коллекции и переключения.

Thread B может вызвать Add/Remove и т.д. до завершения Thread A.