2009-07-20 67 views
4

我收到以下異常,同時通過隊列列舉:System.InvalidOperationException:集合已修改

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()。 問題是,我如何正確實現「模式」?

+4

你爲什麼鎖定隊列?這段代碼對我來說沒有意義。 – 2009-07-20 15:54:15

+0

@Kermit_xc:枚舉器文檔中的重點在於「枚舉器不具有對集合的獨佔訪問權限;因此,枚舉集合本質上不是線程安全的過程。爲了確保枚舉期間的線程安全,您可以鎖定在整個枚舉過程中的集合爲了讓集合可以被多個線程訪問來讀寫,你必須實現自己的同步。「 – 2009-07-20 16:22:43

+0

對。這個代碼甚至沒有做任何事情,甚至含糊不清。 – 2009-07-20 20:11:43

回答

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

的問候,邁克。

+0

這仍然會導致多線程環境中的問題,看來tpotActionQueue是一個「全局」變量,而另一個線程可能會在調用此方法時對其進行修改。 – 2009-07-20 16:00:30

+0

此外,您可以更改集合中的項目,所以我沒有看到這一點。 – 2009-07-20 16:08:32

-1

我想你在迭代它時必須有一些其他線程修改tpotActionQueue。既然你只是在for循環中鎖定這個隊列,這是可能的。

7

我覺得我對集合,我試圖從集合中刪除項目使用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; 
     } 
    } 
} 
1

你沒有一個定義tpotActionQueue,但如果它只是一個正常List<TpotAction>那麼該行是不是你的問題。修改集合是添加或刪除成員 - 不要在包含的對象上設置屬性。

你有一個lock(tpotActionQueue)和一個線程安全的標籤,所以我猜想還有另一個線程在你枚舉時添加或刪除tpotActionQueue中的項目。您可能需要同步這些訪問。

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); 
} 
相關問題