4

私のコードは非常に単純なことをします

リストにはすでに要素があります。リストには約25000個の要素があり(さらに多くの要素があると予想しています)、各要素は小さいです(DateTime)。

List<DateTime> newList = new List<DateTime>();
Parallel.ForEach(list, l => newlist.Add(new DateTime(l.Ticks + 5000)));

つまり、各要素に基づいて、新しい要素を作成し、それらを別のリストに追加しています。しかし、これは良いプログラミングアプローチではないようです。私はこの例外に何度遭遇しましたが、毎回ではありません。

IndexOutOfRangeException : {"Index was outside the bounds of the array."}

Parallel.ForEach()を使用してリストに要素を追加できますか?はいの場合、なぜエラーが発生するのですか?いいえの場合、なぜですか?

4

6 に答える 6

6

この状況で本当に欲しいのは、次のようなものです。

newlist = list.AsParallel().Select(l => new DateTime(l.Ticks + 5000)).ToList();

ただし、パフォーマンスを測定して、この状況が並列化の恩恵を受けるかどうかを確認する必要があります。

于 2012-04-13T23:43:13.743 に答える
5

すべてのスレッドローカル変数を newList に追加する最終結果でスレッドローカル変数を試してみてください...

Parallel.ForEach(list, () => DateTime.MinValue, (l, state, date) =>
{
    date = new DateTime(l.Ticks+5000);
    return date;
},
finalresult =>
{
   lock (newList)
   {
       newList.Add(finalresult);
   }
});

最初のパラメーターは古いリストで、2 番目のパラメーターは各スレッドの初期値です (datetime min に初期化したところです)。3 番目のパラメーター ブロックは次のとおりです。l はコードと同じです。状態は、必要に応じて並列ループを終了できる Paralleloption オブジェクトです。最後は、スレッド ローカル変数を表すスタンドイン変数です。finalresult パラメータは、各スレッド ローカル変数の最終結果を表し、スレッドごとに呼び出されます。ここで、newList をロックして、newList 共有変数に追加できます。理論的には、これは機能します。私は自分のコードで同様のコーディングを使用しました。これがあなたまたは他の誰かに役立つことを願っています。

于 2012-05-30T02:57:44.237 に答える
2

誰もが言及しているように、これを並行して行うケースはないようです。確かに、はるかに遅くなります。ただし、完了するために、これが失敗することがある理由は、複数のスレッドによって書き込まれているリスト オブジェクトにロックがないためです。これを追加:

object _locker = new object();
List<DateTime> newList = new List<DateTime>();
Parallel.ForEach(list, l => lock (_locker) newlist.Add(new DateTime(l.Ticks + 5000)));
于 2012-04-13T23:30:24.420 に答える
2

これは効果的にList<T>.Add同時に呼び出されますが、 MSDN のドキュメントにList<T>よると、次のようになります。

「どのインスタンス メンバーも、スレッド セーフであることが保証されていません。」

たとえそれが (スレッドセーフ) であったとしても、(並列実行のオーバーヘッドとは対照的に) これは並列実行の恩恵を受けるには安すぎます。実際にパフォーマンスを測定しましたか?25000要素はそれほど多くありません。

于 2012-04-13T23:16:18.227 に答える
1

これを使用することを保証するのに十分な作業がParallel.ForEachなくList<T>、スレッドセーフではないため、同じリストに並行して追加する場合はロックする必要があります。通常の for ループを使用するだけです。

于 2012-04-13T23:11:00.300 に答える
1

リストにこれらが本当に必要ですか?foreach でリストを列挙することだけが必要な場合は、メモリの使用量がはるかに少ないため、代わりにこれを行う必要があります。

IEnumerable<DateTime> newSequence = list.Select(d => new DateTime(d.Ticks + 5000));

リストにこれらが本当に必要な場合は、最後に .ToList() を追加してください。

var newSequence = list.Select(d => new DateTime(d.Ticks + 5000)).ToList();

これはほぼ確実に、並列化する必要がないほど十分に高速です。実際、これはメモリ パフォーマンスが向上するため、並列で実行するよりもおそらく高速です。

于 2012-04-13T23:16:59.017 に答える