System.InvalidOperationException:集合已被修改

时间:2009-07-20 15:49:26

标签: c# collections thread-safety

我在通过队列枚举时遇到以下异常:

  

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 个答案:

答案 0 :(得分:8)

我认为在Collection上使用foreach循环时遇到了类似的异常,我试图从Collection中删除项目(或者它可能是List,我不记得了)。我最后使用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;
        }
    }
}

答案 1 :(得分:6)

您可以更改集合中项目的值。你得到的错误意味着一个项目被添加或删除,即:集合本身被修改,而不是集合中的项目。这很可能是由另一个线程在此集合中添加或删除项目引起的。

您应该在方法的开头锁定队列,以防止其他线程在您访问它时修改集合。或者你可以在调用这个方法之前锁定集合。

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);
   }

答案 2 :(得分:1)

您没有tpotActionQueue的定义,但如果它只是正常的List<TpotAction>,则该行不是您的问题。修改集合是添加或删除成员 - 不在包含的对象上设置属性。

你有一个lock(tpotActionQueue)和一个线程安全标记,所以我猜是在你枚举时还有另一个线程在tpotActionQueue添加或删除项目。您可能需要同步这些访问。

答案 3 :(得分:0)

我认为您需要做的就是停止使用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;
        }
     }
}

问候,迈克。

答案 4 :(得分:0)

一些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);
}

答案 5 :(得分:-1)

我认为你在迭代时必须修改tpotActionQueue。由于您只是在for循环中锁定该队列,因此这是可能的。