2009-07-20 16 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

なぜキューをロックしていますか?このコードは私には意味がありません。 –

+0

@Kermit_xc:列挙子のドキュメントの大きなポイントは、「列挙子はコレクションへの排他アクセスを持たないため、コレクションを列挙することは本質的にスレッドセーフな手順ではありません。列挙中にスレッドの安全性を保証するには、コレクション全体を読み書きするために複数のスレッドがアクセスできるようにするには、独自の同期を実装する必要があります。 –

+0

右。このコードはあまりにも漠然としたことをしません。 –

答えて

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

ダング! - ちょうど確認され、コレクション上の要素をプッシュする別のスレッドを持って、ポップはスレッドセーフです。しかし、全体の反復で 'ロック'のパフォーマンスが少し心配です。それはかなり時間に敏感です - それは完全なリワークを必要とするかもしれません:{。しかし、ありがとう! –

+0

@kermit_xs: "ちょうど確認された、コレクション上の要素を押す別のスレッドがある" - それはあなたの問題です。 "foreach"から "for"ループに変更することはあなたの問題ではなく、答えではありません。 foreachを使用するときは、列挙の前にコレクションをロックする必要があります。列挙子はスレッドセーフではありません。 –

0

私はあなたがする必要があるすべてはforeachの使用を中止し、代わりに、ループの

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は「グローバル」変数であり、このメソッドが呼び出されている間に別のスレッドが変更する可能性があります。 –

+0

また、コレクション内のアイテムを変更することが許可されているので、この点についてはわかりません。 –

-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; 
     } 
    } 
} 
+0

はい、ただし、このメソッドはアイテムを削除したりコレクションに追加したりしていません。問題はこのメソッドの外です。 –

+0

ありがとう、それは意味をなさない...それはショットを与えるだろう。 –

+0

いいえStan問題はactionです。ステータスの割り当ては、コレクションの列挙文書を参照してください。列挙子は、コレクションが変更されない限り有効です。要素の追加、変更、削除などのコレクションに変更が加えられた場合、列挙子は回復不能に無効化され、その動作は未定義です。ここで彼は要素を変更しています – SpaceghostAli

1

あなたはtpotActionQueueの定義を持っていないが、それは普通のList<TpotAction>だ場合、その行はあなたの問題ではありません。コレクションを変更するのは、メンバーを追加または削除することです。含まれているオブジェクトのプロパティを設定するのではありません。

あなたはlock(tpotActionQueue)とスレッドセーフのタグを持っていますので、列挙中にtpotActionQueueからアイテムを追加または削除する別のスレッドがあります。おそらくそれらのアクセスを同期させる必要があります。

+0

それは私のアイデアだった - 価値を変更することは、コレクションを台無しにしてはならない。最終的に私はそれをキューから削除したいのではなく、代わりにメインキューのループ内で気にすることができるように 'バッチ処理'としてマークすることを選択しました。 –

+0

プロパティを設定するとカウントされません。http://msdn.microsoft.com/en-us/library/4a9449ty.aspx – SpaceghostAli

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

LINQ - .NET 3.0 maxを実行できません。しかし、良いもの:) –

関連する問題