Почему List ‹T› не является потокобезопасным?

Со следующего сайта:

http://crfdesign.net/programming/top-10-differences-between-java-and-c

К сожалению, List<> не является потокобезопасным (ArrayList в C # и Vector в Java). В C # также есть Hashtable; общая версия:

Что делает List<T> небезопасным для потоков? Это проблема реализации со стороны инженера .NET framework? Или дженерики не являются потокобезопасными?


person Hao    schedule 01.04.2009    source источник
comment
Согласно MSDN ArrayList и List (Of T) имеют такую ​​же безопасность потоков. Однако ArrayList предоставляет оболочку Synchronized.   -  person Mark Hurd    schedule 10.05.2012


Ответы (6)


Вам действительно нужно классифицировать Java Vector по типу потоковой безопасности. Javas Vector безопасно использовать из нескольких потоков, потому что он использует синхронизацию методов. Состояние не будет испорчено.

Однако полезность вектора Java ограничена несколькими потоками без дополнительной синхронизации. Например, рассмотрим простой акт чтения элемента из вектора.

Vector vector = getVector();
if ( vector.size() > 0 ) { 
  object first = vector.get(0);
}

Этот метод не повредит состояние вектора, но он также неверен. Ничто не мешает другому потоку изменить вектор между оператором if и вызовом get (). Этот код может и будет в конечном итоге завершиться ошибкой из-за состояния гонки.

Этот тип синхронизации полезен только в небольшом количестве сценариев и, конечно, стоит недешево. Вы платите заметную цену за синхронизацию, даже если вы не используете несколько потоков.

.Net предпочла не платить эту цену по умолчанию из-за сценария, имеющего лишь ограниченную полезность. Вместо этого он решил реализовать список без блокировок. Авторы несут ответственность за добавление любой синхронизации. Это ближе к модели C ++: «платите только за то, что используете».

Недавно я написал пару статей об опасностях использования коллекций только с внутренней синхронизацией, таких как вектор Java.

Справочник по безопасности потоков вектора: http://www.ibm.com/developerworks/java/library/j-jtp09263.html

person JaredPar    schedule 01.04.2009
comment
Небольшое примечание: без блокировки подразумевается синхронизация без каких-либо блокировок, а не без блокировок. en.wikipedia.org/wiki/Lock_free - person Craig Gidney; 01.04.2009
comment
Я согласен со Стриланком: мне пришлось дважды лишить вас возможности использовать терминологическую блокировку. - person Daniel Fortunov; 18.04.2009
comment
Такие методы, как Count, можно совершенно разумно и законно использовать в поточно-ориентированной коллекции с целью решения не возиться с чем-либо. Например, если нужно выполнить некоторую операцию, используя два элемента, взятых из очереди, вполне разумным шаблоном может быть использование свойства для проверки счетчика (с барьером чтения, а не с блокировкой) и, если счетчик достаточен , получите блокировку и снова проверьте счет. Если этого все еще достаточно, выполните операцию; в противном случае пропустите это. В любом случае разблокируйте замок. - person supercat; 11.05.2012
comment
@supercat ваше решение включает в себя блокировку и, следовательно, является правильным. Мой аргумент заключается в том, что Vector, закодированный в библиотеках java, нельзя безопасно использовать так, как вы описываете, без блокировки. Это просто невозможно, потому что каждый результат, который вы найдете, недействителен в тот момент, когда вы его читаете. - person JaredPar; 12.05.2012
comment
@JaredPar: Я хотел сказать, что такие методы, как Count, часто могут быть полезны вне блокировок, для целей раннего выхода из общих случаев. - person supercat; 12.05.2012
comment
@supercat для чего бы вы его использовали? Я бы очень поспорил, что без блокировки такие методы, как Count, практически бесполезны (за исключением ведения журнала / трассировки). Вы не можете делать никаких прогнозов на основе значения b / c, которое оно может измениться, прежде чем ваш код даже обработает это значение. - person JaredPar; 12.05.2012
comment
@JaredPar: Я только что привел пример: если перед получением блокировки проверяется, содержит ли Count() значение, достаточное для того, что нужно сделать, можно избежать цикла получения / снятия блокировки в случае, если нечего делать. Если нормальное положение дел заключается в том, что сборник не содержит достаточно данных для обработки, такая оптимизация иногда может дать значительные преимущества. Иногда кто-то проверяет Count(), решает получить блокировку, снова проверяет Count() и обнаруживает, что данные исчезли, поэтому больше нечего делать, но ... - person supercat; 12.05.2012
comment
... если это произойдет, единственное последствие - потраченные впустую усилия на получение блокировки. Если можно избежать напрасных усилий в 95% случаев, когда нечего делать, это может быть победой, даже если этого не избежать во всех из них. - person supercat; 12.05.2012
comment
@supercat, это все еще сценарий, в котором все ваше решение требует блокировки. Это то, что я пытаюсь довести до дома своим постом. Я согласен, что в некоторых ограниченных сценариях вызов типа Count может использоваться как дешевый трюк для предотвращения блокировки. Но обратите внимание, что на самом деле это не работает с vector, поскольку сам size получает блокировку. - person JaredPar; 12.05.2012

Почему это будет потокобезопасным? Не каждый класс. Фактически, по умолчанию классы не потокобезопасны.

Потокобезопасность означает, что любая операция, изменяющая список, должна быть заблокирована от одновременного доступа. Это было бы необходимо даже для тех списков, которые будут использоваться только одним потоком. Это было бы очень неэффективно.

person John Saunders    schedule 01.04.2009

Это просто дизайнерское решение реализовать типы, не ориентированные на многопотоковое исполнение. Коллекции предоставляют свойство SyncRoot интерфейса ICollection и метод Synchronized() для некоторых коллекций для явной синхронизации типов данных.

Используйте SyncRoot, чтобы заблокировать объект в многопоточных средах.

lock (collection.SyncRoot)
{
   DoSomething(collection);
}

Используйте collection.Synchronized(), чтобы получить потокобезопасную оболочку для коллекции.

person Daniel Brückner    schedule 01.04.2009
comment
См. Статьи JaredPar для объяснения того, почему это почти наверняка не то, что вы хотите делать. - person Daniel Earwicker; 03.04.2009

Возможность состояния гонки, о которой упоминает JaredPar, является пугающим следствием предполагаемой потоковой безопасности Vector. Такие вещи приводят к тому, что «каждый девятый вторник приложение делает что-то странное» - отчет о дефектах, который сводит вас с ума.

В .Net входит ряд действительно потоковобезопасных коллекций 4, с интересным побочным эффектом, заключающимся в том, что они позволяют использовать однопоточную модификация коллекции при перечислении, но есть снижение производительности, связанное с безопасностью потоков, иногда весьма значительным.

Таким образом, логично, что разработчик фреймворка должен поддерживать максимальную производительность класса для 95% пользователей, которые, вероятно, не будут использовать многопоточность, и полагаться на тех, кто использует многопоточность, чтобы знать, что им нужно делать, чтобы сохранить это безопасно.

person Ragoczy    schedule 03.04.2009

Для истинной безопасности потоков List<> и другие типы коллекций должны быть неизменными. С появлением параллельных расширений .NET в .NET 4.0 мы увидим поточно-ориентированные версии наиболее часто используемых коллекций. Джон Скит затрагивает некоторые из этих вопросов.

person Abhijeet Patel    schedule 01.04.2009

используйте SynchronizedCollection, он также предоставляет параметр-конструктор для использования общей синхронизации :)

person mo.    schedule 29.01.2010