大量のデータを処理するためのコードを書いていたので、Parallel.ForEachで作成するスレッドごとにファイルを作成して、出力を同期する必要がないようにすると便利だと思いました(少なくとも私は)。
これは次のようになります。
Parallel.ForEach(vals,
new ParallelOptions { MaxDegreeOfParallelism = 8 },
()=>GetWriter(), // returns a new BinaryWriter backed by a file with a guid name
(item, state, writer)=>
{
if(something)
{
state.Break();
return writer;
}
List<Result> results = new List<Result>();
foreach(var subItem in item.SubItems)
results.Add(ProcessItem(subItem));
if(results.Count > 0)
{
foreach(var result in results)
result.Write(writer);
}
return writer;
},
(writer)=>writer.Dispose());
私が予想したことは、最大8つのファイルが作成され、実行時間全体にわたって存続することでした。その後、ForEach呼び出し全体が終了すると、それぞれが破棄されます。実際に起こることは、localInitがアイテムごとに1回呼び出されるように見えるため、何百ものファイルが作成されることになります。ライターは、処理される各アイテムの最後にも廃棄されます。
これは、同じことが起こっていることを示しています。
var vals = Enumerable.Range(0, 10000000).ToArray();
long sum = 0;
Parallel.ForEach(vals,
new ParallelOptions { MaxDegreeOfParallelism = 8 },
() => { Console.WriteLine("init " + Thread.CurrentThread.ManagedThreadId); return 0L; },
(i, state, common) =>
{
Thread.Sleep(10);
return common + i;
},
(common) => Interlocked.Add(ref sum, common));
そうか:
init 10
init 14
init 11
init 13
init 12
init 14
init 11
init 12
init 13
init 11
... // hundreds of lines over < 30 seconds
init 14
init 11
init 18
init 17
init 10
init 11
init 14
init 11
init 14
init 11
init 18
注:Thread.Sleep呼び出しを省略した場合、「正しく」機能しているように見えることがあります。localInitは、PCで使用することを決定した4つのスレッドに対してそれぞれ1回だけ呼び出されます。ただし、毎回ではありません。
これは関数の望ましい動作ですか?これを行う原因となる舞台裏で何が起こっているのでしょうか?そして最後に、私の希望する機能であるThreadLocalを取得するための良い方法は何ですか?
ちなみに、これは.NET4.5にあります。