Вопрос:

Потокобезопасный foreach перечисление списков

c# multithreading

12753 просмотра

10 ответа

14504 Репутация автора

Мне нужно перечислить хотя бы общий IList <> объектов. Содержимое списка может измениться, как при добавлении или удалении другими потоками, и это убьет мое перечисление: «Коллекция была изменена; операция перечисления может не выполняться».

Что такое хороший способ сделать потокобезопасный foreach в IList <>? желательно без клонирования всего списка. Невозможно клонировать реальные объекты, на которые ссылается список.

Автор: Radu094 Источник Размещён: 15.09.2008 08:29

Ответы (10)


1 плюс

59443 Репутация автора

Фореч зависит от того, что коллекция не изменится. Если вы хотите перебрать коллекцию, которая может измениться, используйте нормальную конструкцию for и будьте готовы к недетерминированному поведению. Блокировка может быть лучшей идеей, в зависимости от того, что вы делаете.

Автор: Patrick Размещён: 15.09.2008 08:31

2 плюса

32051 Репутация автора

Там нет такой операции. Лучшее, что вы можете сделать, это


lock(collection){
    foreach (object o in collection){
       ...
    }
}
Автор: Jason Punyon Размещён: 15.09.2008 08:32

12 плюса

156569 Репутация автора

Решение

Клонирование списка - это самый простой и лучший способ, поскольку он гарантирует, что ваш список не изменится из-под вас. Если список слишком велик, чтобы его можно было клонировать, подумайте о том, чтобы установить вокруг него блокировку, которую необходимо сделать перед чтением / записью.

Автор: John Millikin Размещён: 15.09.2008 08:33

0 плюса

3341 Репутация автора

Оберните список в блокирующий объект для чтения и записи. Вы даже можете выполнять итерацию с несколькими читателями одновременно, если у вас есть подходящая блокировка, которая позволяет одновременно работать с несколькими читателями, а также с одним писателем (когда читателей нет).

Автор: jamuraa Размещён: 15.09.2008 08:33

3 плюса

1468 Репутация автора

Ваша проблема в том, что перечисление не позволяет IList измениться. Это означает, что вы должны избегать этого при просмотре списка.

На ум приходит несколько возможностей:

  • Клонировать список. Теперь у каждого перечислителя есть своя собственная копия для работы.
  • Сериализация доступа к списку. Используйте блокировку, чтобы убедиться, что никакой другой поток не может изменить ее во время перечисления.

В качестве альтернативы, вы можете написать свою собственную реализацию IList и IEnumerator, которая обеспечивает необходимый вам параллельный доступ. Тем не менее, я боюсь, что это не будет просто.

Автор: jan.vdbergh Размещён: 15.09.2008 08:35

1 плюс

11924 Репутация автора

ICollection MyCollection;
// Instantiate and populate the collection
lock(MyCollection.SyncRoot) {
  // Some operation on the collection, which is now thread safe.
}

Из MSDN

Автор: Forgotten Semicolon Размещён: 15.09.2008 08:36

3 плюса

31781 Репутация автора

Вы найдете это очень интересная тема.

Наилучший подход основан на ReadWriteResourceLock, который используется для решения проблем с производительностью из-за так называемой проблемы Convoy.

Лучшая статья, которую я нашел, касающаяся этой темы, - это статья Джеффри Рихтера, которая предлагает собственный метод для высокопроизводительного решения.

Автор: Jorge Córdoba Размещён: 15.09.2008 08:45

1 плюс

46629 Репутация автора

Итак, требования таковы: вам нужно перечислять через IList <>, не создавая копию, одновременно добавляя и удаляя элементы.

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

Это, безусловно, выполнимо путем создания пользовательского объекта IEnumerable с, возможно, целочисленным индексом, но только если вы можете контролировать весь доступ к вашему объекту IList <> (для блокировки и поддержания состояния вашего перечисления). Но многопоточное программирование - сложный бизнес в лучших обстоятельствах, и это сложная вероятность.

Автор: Jeffrey L Whitledge Размещён: 16.09.2008 01:47

1 плюс

0 Репутация автора

Поведение по умолчанию для простой структуры индексированных данных, такой как связанный список, b-дерево или хеш-таблица, состоит в перечислении в порядке от первого до последнего. Это не вызовет проблемы при вставке элемента в структуру данных после того, как итератор уже прошел эту точку, или при вставке элемента, который итератор будет перечислять после его достижения, и такое событие может быть обнаружено приложением и обработано, если Приложение требовало этого. Чтобы обнаружить изменение в коллекции и выдать ошибку во время перечисления, я мог только представить, что это была чья-то (плохая) идея сделать то, что, как они думали, захочет программист. Действительно, Microsoft исправила их коллекции для правильной работы. Они назвали свои блестящие новые непрерывные коллекции ConcurrentCollections (System.Collections.Concurrent) в .NET 4.0.

Автор: Venting Programmer Размещён: 06.07.2009 08:54

0 плюса

10 Репутация автора

Недавно я потратил некоторое время на многопоточность большого приложения, и у меня было много проблем с foreach, работающим со списком объектов, разделяемых между потоками.

Во многих случаях вы можете использовать старый добрый цикл for и немедленно назначить объект копии для использования внутри цикла. Просто имейте в виду, что все потоки, записывающие объекты вашего списка, должны записывать разные данные объектов. В противном случае используйте блокировку или копию, как предлагают другие участники.

Пример:

foreach(var p in Points)
{
    // work with p...
}

Можно заменить на:

for(int i = 0; i < Points.Count; i ++)
{
   Point p = Points[i];
   // work with p...
}
Автор: folmerbrem Размещён: 22.02.2019 01:41
Вопросы из категории :
32x32