Мне нужна реализация List<T>
as свойства, которое можно без сомнения использовать поточно.
Что-то вроде этого:
private List<T> _list;
private List<T> MyT
{
get { // return a copy of _list; }
set { _list = value; }
}
Кажется, мне все еще нужно вернуть копию (клонированную) коллекции, поэтому, если где-то мы выполняем итерацию коллекции, и в то же время коллекция установлена, то исключение не возникает.
Как реализовать потокобезопасное свойство коллекции?
c#
collections
properties
thread-safety
Xaqron
источник
источник
IList<T>
(vsList<T>
)?List<T>
орудует? Если да, то не могли бы вы предоставить интерфейс, который вам нужен, вместо того, чтобы спрашивать обо всем, чтоList<T>
уже есть?Ответы:
Если вы ориентируетесь на .Net 4, в пространстве имен System.Collections.Concurrent есть несколько вариантов.
Вы можете использовать
ConcurrentBag<T>
в этом случае вместоList<T>
источник
ConcurrentBag
- это неупорядоченная коллекция, поэтому в отличие отList<T>
нее не гарантируется порядок. Также вы не можете получить доступ к элементам по индексу.Несмотря на то, что он получил наибольшее количество голосов, его обычно нельзя рассматривать
System.Collections.Concurrent.ConcurrentBag<T>
как поточно-System.Collections.Generic.List<T>
ориентированную замену, поскольку она (Радек Стромски уже указал на это) не заказана.Но есть класс с именем,
System.Collections.Generic.SynchronizedCollection<T>
который уже существует с .NET 3.0 как часть фреймворка, но он так хорошо спрятан в месте, где никто не ожидает, что он малоизвестен, и, вероятно, вы никогда не сталкивались с ним (по крайней мере, Я никогда не делал).SynchronizedCollection<T>
компилируется в сборку System.ServiceModel.dll (которая является частью клиентского профиля, но не переносимой библиотеки классов).Надеюсь, это поможет.
источник
Я бы подумал, что создать образец класса ThreadSafeList будет легко:
Вы просто клонируете список перед тем, как запрашивать счетчик, и, таким образом, любое перечисление работает с копии, которую нельзя изменить во время работы.
источник
T
это ссылочный тип, не вернет ли он просто новый список, содержащий ссылки на все исходные объекты? Если это так, этот подход все равно может вызвать проблемы с потоками, поскольку к объектам списка могут обращаться несколько потоков через разные «копии» списка.newList
не нужно добавлять или удалять какие-либо элементы, которые сделали бы счетчик недействительным).Даже принятый ответ - ConcurrentBag, я не думаю, что это реальная замена списка во всех случаях, поскольку в комментарии Радека к ответу говорится: «ConcurrentBag - это неупорядоченная коллекция, поэтому, в отличие от List, она не гарантирует упорядочение. Также вы не можете получить доступ к элементам по индексу. ».
Поэтому, если вы используете .NET 4.0 или выше, обходным решением может быть использование ConcurrentDictionary с целым числом TKey в качестве индекса массива и TValue в качестве значения массива. Это рекомендуемый способ замены list в курсе Pluralsight по параллельным коллекциям C # . ConcurrentDictionary решает обе проблемы, упомянутые выше: доступ к индексу и упорядочивание (мы не можем полагаться на упорядочение, поскольку это хэш-таблица под капотом, но текущая реализация .NET сохраняет порядок добавления элементов).
источник
ConcurrentDictionary
это словарь, а не список. 2) Сохранение порядка не гарантируется, как указано в вашем собственном ответе, что противоречит указанной вами причине публикации ответа. 3) Он ссылается на видео без соответствующих цитат в этом ответе (что в любом случае может не соответствовать их лицензированию).current implementation
если это явно не гарантируется документацией. Реализация может быть изменена в любое время без предварительного уведомления.У
ArrayList
класса C # естьSynchronized
метод.Это возвращает потокобезопасную оболочку вокруг любого экземпляра
IList
. Все операции необходимо выполнять через обертку, чтобы обеспечить безопасность потока.источник
Если вы посмотрите исходный код для List of T ( https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,c66df6f36c131877 ), вы заметите, что там есть класс (который, конечно, внутренний - почему, Microsoft, почему?!?!) называется SynchronizedList of T. Я копирую вставку кода здесь:
Лично я думаю, что они знали, что можно создать лучшую реализацию с использованием SemaphoreSlim , но не дошли до нее.
источник
_root
) при каждом доступе (чтение / запись) делает это решение медленным. Может быть, лучше оставить этот класс внутренним.Clear()
после вызовов другого,this[index]
но до активации блокировки.index
более небезопасен в использовании и вызовет исключение при окончательном выполнении.Вы также можете использовать более примитивный
какую блокировку использует (см. этот пост C # Блокировка объекта, который переназначен в блоке блокировки ).
Если вы ожидаете исключения в коде, это небезопасно, но позволяет делать что-то вроде следующего:
Одна из приятных особенностей этого заключается в том, что вы получите блокировку на время серии операций (а не блокировку в каждой операции). Это означает, что вывод должен выводиться правильными фрагментами (я использовал это для вывода на экран какого-либо внешнего процесса)
Мне очень нравится простота + прозрачность ThreadSafeList +, который играет важную роль в предотвращении сбоев.
источник
В .NET Core (любой версии) вы можете использовать ImmutableList , который имеет все функции
List<T>
.источник
Я верю
_list.ToList()
сделаю вам копию. Вы также можете запросить его, если вам нужно, например:В любом случае, msdn говорит, что это действительно копия, а не просто ссылка. О, и да, вам нужно будет заблокировать метод set, как указывали другие.
источник
Похоже, что многие люди, обнаружившие это, хотят иметь поточно-безопасную индексированную коллекцию динамического размера. Самым близким и простым, что я знаю, было бы.
Для этого потребуется убедиться, что ваш ключ правильно инкриминирован, если вы хотите нормального поведения индексации. Если вы будете осторожны .count может быть достаточно в качестве ключа для любых новых пар ключ-значение, которые вы добавляете.
источник
Я бы посоветовал всем, кто имеет дело с
List<T>
многопоточными сценариями, взглянуть на Immutable Collections, в частности на ImmutableArray .Я нахожу это очень полезным, когда у вас есть:
Также может быть полезно, когда вам нужно реализовать какое-то поведение, подобное транзакции (например, отменить операцию вставки / обновления / удаления в случае сбоя)
источник
Вот класс, о котором вы просили:
источник
this.GetEnumerator();
когда предлагает @Tejsthis.Clone().GetEnumerator();
?[DataContract( IsReference = true )]
?В принципе, если вы хотите безопасно перечислить, вам нужно использовать lock.
Пожалуйста, обратитесь к MSDN по этому поводу. http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx
Вот часть MSDN, которая может вас заинтересовать:
Открытые статические (общие в Visual Basic) члены этого типа являются потокобезопасными. Потокобезопасность любых членов экземпляра не гарантируется.
Список может поддерживать несколько читателей одновременно, пока коллекция не изменяется. По сути, перечисление через коллекцию не является поточно-ориентированной процедурой. В редких случаях, когда перечисление соперничает с одним или несколькими доступами на запись, единственный способ обеспечить безопасность потоков - это заблокировать коллекцию на протяжении всего перечисления. Чтобы разрешить доступ к коллекции нескольким потокам для чтения и записи, вы должны реализовать свою собственную синхронизацию.
источник
Вот класс для безопасного списка потоков без блокировки
источник
Используйте для этого
lock
инструкцию. ( Подробнее читайте здесь. )К вашему сведению, это, вероятно, не совсем то, о чем вы спрашиваете - вы, вероятно, захотите заблокировать больше в своем коде, но я не могу этого предположить. Взгляните на
lock
ключевое слово и адаптируйте его использование к вашей конкретной ситуации.Если вам нужно, вы можете
lock
как в блоке, такget
и вset
блоке, используя_list
переменную, которая сделает так, чтобы чтение / запись не происходило одновременно.источник
lock
утверждение. Используя аналогичный пример, я мог бы утверждать, что нам не следует использовать циклы for, поскольку вы можете заблокировать приложение без каких-либо усилий:for (int x = 0; x >=0; x += 0) { /* Infinite loop, oops! */ }
lock (this)
иlock (typeof(this))
являются большими «нет».