2016-09-05 12 views
0

List<string>の並列処理には1つの方法があります。残念ながら.NET 4+は使用できません。ManualResetEventによる並列プログラミング

しかし、私はこの方法を実行すると、iはいつも私が

X 0
×1
×2

を達成するために変更する必要が何items.Count

public static void ParallelForEachTest(List<string> items) 
{ 
    if (items != null && items.Count > 0) 
    { 
     List<ManualResetEvent> mEventList = new List<ManualResetEvent>(); 
     for (int i = 0; i < items.Count ; i++) 
     { 
      ManualResetEvent mEvent = new ManualResetEvent(false); 
      ThreadPool.QueueUserWorkItem((y) => 
      { 
       Console.WriteLine(items[i] + i); 
       mEvent.Set(); 
      }); 
      mEventList.Add(mEvent); 
     } 
     mEventList.ForEach(x => x.WaitOne()); 
    } 
} 

ですParallelForEachTest(new List<string>(){"x","x","x"});

+0

奇妙な方法。私はあなたの並列化されたコードにパラメータとしてiの値を渡すことを検討する必要があると思う... – Ian

+0

標準的なバグ、それはlamba式のforループ変数をキャプチャします。 [これを読む](https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/)。スレッドが実行を開始するまでに、 'i'変数はすでにインクリメントされています。通常。 'var index = i;'を使用し、インデックスを取得します。 –

答えて

2

Console.WriteLineが実行された時点で、ループは既に終了しており、ループ変数iの最終値はlist.Countです。

各タスクは、それに対応するインデックスを印刷持つためには、タスク自体にインデックス値を渡す必要があります:

ThreadPool.QueueUserWorkItem((y) => { ... }, i); 

2番目の引数はタスクたら、コールバックに渡される状態であり、が開始される。

また、クロージャを使用してタスク内から状態にアクセスしないようにしてください。タスク状態を使用してデータを転送する:

public static void ParallelForEachTest(List<string> items) 
{ 
    if (items != null && items.Count > 0) 
    { 
     List<ManualResetEvent> mEventList = new List<ManualResetEvent>(); 
     for (int i = 0; i < items.Count; i++) 
     { 
      ManualResetEvent mEvent = new ManualResetEvent(false); 
      ThreadPool.QueueUserWorkItem(
       (state) => 
       { 
        Tuple<string, int, ManualResetEvent> tuple = 
         (Tuple<string, int, ManualResetEvent>)state; 
        Console.WriteLine(tuple.Item1 + tuple.Item2); 
        tuple.Item3.Set(); 
       }, 
       Tuple.Create(items[i], i, mEvent)); 
      mEventList.Add(mEvent); 
     } 
     mEventList.ForEach(x => x.WaitOne()); 
    } 
} 
+0

私のアプローチを削除します。 +1 – fubo

+0

ありがとうございます。明確にするために、C#5ではforeachが変更されました:すべての反復で* new *変数が割り当てられます。クロージャでループ変数をキャプチャすると、C#5以降では、各繰り返しで新しい変数が与えられます。 v 5より前では、すべての反復で同じ変数を取得してから失敗するタスクを取得しました。それは奇妙な動作につながる可能性がありますので、ループ変数をキャプチャしないように一般的なアドバイスです。 –

関連する問題