Как изменить коллекцию foreach - PullRequest
2 голосов
/ 21 апреля 2009

Как узнать, какая функция или цель изменяет элементы в цикле foreach в многопоточном приложении?

Я постоянно получаю сообщение об ошибке «Коллекция была изменена; операция перечисления может не выполняться». Я не удаляю и не добавляю никаких элементов в общий список в цикле for. Я хочу узнать, как это изменяется. Каков наилучший способ сделать это?

Спасибо

Ответы [ 8 ]

7 голосов
/ 21 апреля 2009

Подожди минутку - думаю, мы все упустили момент.

Классы коллекции не являются поточно-ориентированными. Если доступ к коллекции осуществляется несколькими потоками без блокировки потоков, вам необходимо это исправить. Это особенно коварная проблема, потому что она будет работать правильно [редактировать - или выдавать предсказуемое исключение] 99,999% времени, а в 0,001% случаев она будет делать что-то совершенно непредсказуемое и почти невозможно воспроизвести.

Простой подход заключается в использовании операторов lock {} в КАЖДОМ месте, где ЛЮБОЙ код получает доступ к коллекции. Это может быть небольшим излишним, но это самый безопасный план. Для итерации вы можете либо поставить блокировку вокруг всего цикла, либо, если вы не хотите блокировать другие потоки так долго, просто заблокируйте их достаточно долго, чтобы сделать снимок:

object[] snap;
lock (list)
{
   snap = list.ToArray();
}
foreach (object x in snap) ...
2 голосов
/ 21 апреля 2009

Это не прямой ответ, а только предложение, которое может помочь - в многопоточной ситуации, когда коллекция может быть изменена во время итерации, я обычно использую метод ToArray, чтобы создать снимок списка для итерации:

foreach (object x in list.ToArray()) ...
1 голос
/ 21 апреля 2009

Замените коллекцию ObservableCollection , прикрепите события к изменениям и прервите их в отладчике (или запишите трассировку стека и т. Д.).

0 голосов
/ 21 апреля 2009

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

0 голосов
/ 21 апреля 2009

Для этого вы можете попробовать класс ObservableCollection . Есть несколько примеров того, как сделать этот поток безопасным.

0 голосов
/ 21 апреля 2009

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

0 голосов
/ 21 апреля 2009

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

0 голосов
/ 21 апреля 2009

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

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

...