基本的に、私はこれを使用しています:
_var data = input.AsParallel();
List<String> output = new List<String>();
Parallel.ForEach<String>(data, line => {
String outputLine = "";
// ** Do something with "line" and store result in "outputLine" **
// Additionally, there are some this.Invoke statements for updating UI
output.Add(outputLine);
});
_
入力は_List<String>
_オブジェクトです。 ForEach()
ステートメントは、各値に対して何らかの処理を実行し、UIを更新して、結果をoutput
List
に追加します。これに本質的に何か問題がありますか?
注:
更新:
私が得たフィードバックに基づいて、手動のlock
を_output.Add
_ステートメントとUI更新コードに追加しました。
はい; List<T>
はスレッドセーフではないため、任意のスレッドからアドホックに(おそらく同時に)追加することはできません。代わりにスレッドセーフリストを使用するか、手動でロックを追加する必要があります。または、Parallel.ToList
があるかもしれません。
また、重要な場合:挿入順序は保証されません。
このバージョンis安全ですが:
var output = new string[data.Count];
Parallel.ForEach<String>(data, (line,state,index) =>
{
String outputLine = index.ToString();
// ** Do something with "line" and store result in "outputLine" **
// Additionally, there are some this.Invoke statements for updating UI
output[index] = outputLine;
});
ここでは、index
を使用して、並列呼び出しごとに異なる配列インデックスを更新しています。
これに本質的に何か問題がありますか?
はい、すべてです。これはどれも安全ではありません。リストは複数のスレッドで同時に更新するのは安全ではなく、UIスレッド以外のスレッドからUIを更新することはできません。
ドキュメント _List<T>
_のスレッドセーフについて次のように述べています。
このタイプのパブリック静的(Visual Basicで共有)メンバーはスレッドセーフです。インスタンスメンバーは、スレッドセーフであることが保証されていません。
List(Of T)は、コレクションが変更されていない限り、複数のリーダーを同時にサポートできます。コレクションを介して列挙することは、本質的にスレッドセーフな手順ではありません。列挙型が1つ以上の書き込みアクセスと競合するまれなケースでは、スレッドセーフを確保する唯一の方法は、列挙型全体でコレクションをロックすることです。読み取りと書き込みのために複数のスレッドがコレクションにアクセスできるようにするには、独自の同期を実装する必要があります。
したがって、output.Add(outputLine)
はnotスレッドセーフであり、たとえば、add操作をでラップすることによってスレッドセーフを自分で確保する必要があります。 lock
ステートメント。