Является ли 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.