2011-09-10 20 views
3

私はBackgroundWorkerを使っていつもIEnumerator<T> listを処理する小さなアプリケーションを持っています。反復処理中にListItemをリストに追加する方法(C#)

コードは基本的にこのような:

while(true){ 
    foreach(T item in list){ 
     // Process each item and send process 
     // Add an object in child List (List<T1> item.Result) 
    } 
    Thread.Sleep(500); 
} 

今、私はIEnumeratorを直接追加しますこれ、ボタンやテキストボックスを持っています。

問題は、ボタンを追加した後、バックグラウンド作業者は現在処理中のアイテムを処理し続けますが、そのアイテムを終了した後は停止するということです。それは続かない。

項目を背景に影響を与えずにリストに安全に追加するにはどうすればよいですか?バックグラウンドワーカーの横にもアイテムにオブジェクトを追加します。このための解決策は何でしょうか?

はおそらくsimultaniouslyコードの両方の場所から共有「リスト」変数にアクセスしないようにする「ロック」キーワードを使用する必要が

答えて

6

元のリストのコピーではなく、リスト自体の上にバックグラウンドワーカー反復を持っています。

while (true) 
{ 
     foreach (T item in new List<T>(list)) 
     { 
      .... 
     } 
     Thread.Sleep(500); 
} 

あなたはそれの上に列挙しながら、コレクションを変更しようとすると、列挙子は例外をスローします。 docsから:コレクションが変更されない のままとして

列挙子は限り有効です。追加、 要素の変更または削除などのコレクションに変更が加えられた場合、列挙子は無効になり、 が無効になり、次のMoveNextまたはResetの呼び出しによって InvalidOperationExceptionがスローされます。コレクションが MoveNextとCurrentの間で変更された場合、列挙子が既に無効化されていても、Currentはそれが設定されている要素 を返します。

+0

はい、私は列挙子を変更しません。私が編集しているのはitem.Resultです。 itemListには影響しません。私がこれをコピーすると。結果は変更されません。 – DucDigital

+1

@Duc - リストのコピーには元のオブジェクトと同じオブジェクトへの参照が含まれているので、コピー内のオブジェクトを変更した場合は元のコピーを変更します(Tは値型ではないと仮定します)。あなたは反復中にリストに追加することについて尋ねました。これは、列挙子の基になるコレクションを変更し、それを無効にし、次回の移動時に例外をスローします。 – tvanfosson

+0

これはうまく動作します。ありがとう、tvanfosson – DucDigital

1
+0

ロックを入れるだけです。しかし、プログラムは、リストに追加する前に、すべての項目が完了するまで待つことができます。それは動作しますが、基本的にユーザーエクスペリエンスにとって理想的ではありません。 :) – DucDigital

2

まず、マルチスレッドプログラミングの基本を学ぶ必要があります。

がこの沿って何かを試してみてください:

// shared queue 
ConcurrentQueue<T> queue = new ConcurrentQueue<T>(); 
// shared wait handle 
AutoResetEvent autoEvent = new AutoResetEvent(); 

それはあなたが現在の要素のインデックスを気にせずにそれから要素を追加したり削除することができますので、キューは、ここにリストよりも優れている - あなただけのEnqueue()アイテムもう片方にはDequeue()があります。 System.Collections.Concurrent名前空間のクラスを使用すると、スレッドセーフなアクセスが自動的に処理されます(また、複雑な理由により、後で読みたい場合があるため、単純なlock()よりも高速です)。

は今、フォアグラウンドスレッド:

// schedule the work 
queue.Enqueue(itemOfWork); 
// and wake up our worker 
autoEvent.Set(); 

ここsprinkly-魔法の一部は(はい、AutoResetEventWaitHandleの実装です)私たちのWaitHandleで呼び出さSet()です。Thread.Sleep()のような醜い構文を使わずに、同期イベントが発生するのを待っていた1つのスレッドを起動します。 Sleep()への呼び出しは、ほとんどの場合、マルチスレッドコードの間違いの兆候です。

最後の部分は、ワーカースレッドです。ここにあまり変更がありません:

while(true) 
{ 
    // wait for the signal 
    autoEvent.WaitOne(); 
    T item; 
    // grab the work item 
    queue.TryDequeue(out item); 

    // handle the item here; 
} 
関連する問題