System.InvalidOperationException: коллекция была изменена - PullRequest
5 голосов
/ 20 июля 2009

При перечислении через очередь я получаю следующее исключение:

System.InvalidOperationException: Коллекция была изменена; перечисление операция может не выполняться

Вот выдержка из кода:

1:    private bool extractWriteActions(out List<WriteChannel> channelWrites)
2:    {
3:        channelWrites = new List<WriteChannel>();
4:        foreach (TpotAction action in tpotActionQueue)
5:        {
6:            if (action is WriteChannel)
7:            {
8:                channelWrites.Add((WriteChannel)action);
9:                lock(tpotActionQueue)
10:               {
11:                  action.Status = RecordStatus.Batched;
12:               }
13:           }
14:       }
15:       return (channelWrites.Count > 0);
16:   }

Мне кажется, я понимаю проблему - изменив хеш-таблицу на action.Status = RecordStatus.Batched, что приводит к повреждению MoveNext () на перечислителе. Вопрос в том, как правильно реализовать этот «шаблон»?

Ответы [ 6 ]

8 голосов
/ 20 июля 2009

Я думаю, что у меня было похожее исключение при использовании цикла foreach в Коллекции, когда я пытался удалить элементы из Коллекции (или это мог быть Список, я не помню). Я закончил тем, что обошел это используя цикл for. Возможно, попробуйте что-то вроде следующего:

for (int i=0; i<tpotActionQueue.Count(); i++)
{
    TpotAction action = tpotActionQueue.Dequeue();
    if (action is WriteChannel)
    {
        channelWrites.Add((WriteChannel)action);
        lock(tpotActionQueue)
        {
            action.Status = RecordStatus.Batched;
        }
    }
}
6 голосов
/ 20 июля 2009

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

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

private bool extractWriteActions(out List<WriteChannel> channelWrites)
    {
      lock(tpotActionQueue)
      {
        channelWrites = new List<WriteChannel>();
        foreach (TpotAction action in tpotActionQueue)
        {
            if (action is WriteChannel)
            {
                channelWrites.Add((WriteChannel)action);

                  action.Status = RecordStatus.Batched;

           }
        }
      }
       return (channelWrites.Count > 0);
   }
1 голос
/ 20 июля 2009

У вас нет определения для tpotActionQueue, но если это просто нормальное List<TpotAction>, то эта строка не ваша проблема. Изменение коллекции - это добавление или удаление членов, а не установка свойства для содержащегося объекта.

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

0 голосов
/ 20 июля 2009

Как насчет блага LINQy?

private bool extractWriteActions(out List<WriteChannel> channelWrites)
{

   channelWrites= tpotActionQueue.Where<WriteChannel>(x => x is WriteChannel).ToList()

   foreach(WriteChannel channel in channelWrites) {
      channel.Status = RecordStatus.Batched;
   }

  return ( channelWrites.Count > 0);
}
0 голосов
/ 20 июля 2009

Я думаю, у вас должен быть какой-то другой поток, модифицирующий tpotActionQueue, пока вы итерируете его. Поскольку вы только блокируете эту очередь внутри цикла for, это возможно.

0 голосов
/ 20 июля 2009

Я думаю, все, что вам нужно сделать, это прекратить использование foreach и вместо этого переключить его на цикл for

for(int i = 0; i < tpotActionQueue.Length; i++)
{
     TpotAction action = tpotActionQueue[i];

     if (action is WriteChannel)
     {
        channelWrites.Add((WriteChannel)action);
        lock(tpotActionQueue)
        {
           action.Status = RecordStatus.Batched;
        }
     }
}

С уважением, Майк.

...