1

リストとリスト内の各項目に対して Parallel.ForEach を使用して、データベース呼び出しを試みています。エラーの有無にかかわらず、各項目をログに記録しようとしています。ここで専門家に確認したかったのですが、私が正しい方法で物事を行っているかどうか. この例では、データベース アクセスの代わりにファイル アクセスを使用して I/O をシミュレートしています。

    static ConcurrentQueue<IdAndErrorMessage> queue = new ConcurrentQueue<IdAndErrorMessage>();
    private static void RunParallelForEach()
    {
      List<int> list = Enumerable.Range(1, 5).ToList<int>();
      Console.WriteLine("Start....");
      Stopwatch stopWatch = new Stopwatch();
      stopWatch.Start();
      Parallel.ForEach(list, (tempId) =>
      {
        string errorMessage = string.Empty;
        try
        {
          ComputeBoundOperationTest(tempId);
           try
           {
              Task[] task = new Task[1]
              {
               Task.Factory.StartNew(() =>  this.contentFactory.ContentFileUpdate(content, fileId))
              };
           }
           catch (Exception ex)
           {
              this.tableContentFileConversionInfoQueue.Enqueue(new ContentFileConversionInfo(fileId, ex.ToString()));
           }
        }
        catch (Exception ex)
        {
          errorMessage = ex.ToString();
        }
        if (queue.SingleOrDefault((IdAndErrorMessageObj) => IdAndErrorMessageObj.Id == tempId) == null)
        {
            queue.Enqueue(new IdAndErrorMessage(tempId, errorMessage));
        }
     }
     );
     Console.WriteLine("Stop....");
     Console.WriteLine("Total milliseconds :- " + stopWatch.ElapsedMilliseconds.ToString());
}

以下はヘルパーメソッドです:-

private static byte[] FileAccess(int id)
{
    if (id == 5)
    {
      throw new ApplicationException("This is some file access exception");
    }
     return File.ReadAllBytes(Directory.GetFiles(Environment.SystemDirectory).First());
            //return File.ReadAllBytes("Files/" + fileName + ".docx");
}

 private static void ComputeBoundOperationTest(int tempId)
 {
    //Console.WriteLine("Compute-bound operation started for :- " + tempId.ToString());
    if (tempId == 4)
    {
       throw new ApplicationException("Error thrown for id = 4 from compute-bound operation");
    }
    Thread.Sleep(20);
 }

 private static void EnumerateQueue(ConcurrentQueue<IdAndErrorMessage> queue)
 {
    Console.WriteLine("Enumerating the queue items :- ");
    foreach (var item in queue)
    {
      Console.WriteLine(item.Id.ToString() + (!string.IsNullOrWhiteSpace(item.ErrorMessage) ? item.ErrorMessage : "No error"));
    }
 }
4

3 に答える 3

2

これを行う理由はありません:

/*Below task is I/O bound - so do this Async.*/
Task[] task = new Task[1]
{
    Task.Factory.StartNew(() => FileAccess(tempId))
};
Task.WaitAll(task);

これを別のタスクでスケジュールし、すぐに待機することで、より多くのスレッドを拘束するだけです。これを次のようにしておく方がよいでしょう:

/*Below task is I/O bound - but just call it.*/
FileAccess(tempId);

そうは言っても、すべての項目に対してログに記録された値 (例外または成功) を作成していることを考えると、これをメソッドに書き込んでから、全体を PLINQ クエリとして呼び出すことを検討することをお勧めします。

たとえば、try/catch (スレッドなし) を処理するメソッドにこれを記述し、「ログに記録された文字列」を返す場合、つまり:

string ProcessItem(int id) { // ...

操作全体を次のように記述できます。

var results = theIDs.AsParallel().Select(id => ProcessItem(id));
于 2012-08-15T18:05:47.483 に答える
1

Console.WriteLineスレッド コードから削除することをお勧めします。理由は、Windows アプリごとに 1 つのコンソールしか存在できないためです。そのため、2 つ以上のスレッドがコンソールに並行して書き込みを行う場合、1 つが待機する必要があります。

カスタム エラー キューの代わりに、 .NET 4 の Aggregate Exceptionを確認してそれをキャッチし、それに応じて例外を処理したい場合があります。プロパティは、必要な例外のInnerExceptionsリストを提供します。詳細はこちら

また、一般的なコード レビューのコメントとして、次のようなマジック ナンバーを使用しないでください4if (tempId == 4)代わりに、4 が何を表すかを示す const を定義します。例えばif (tempId == Error.FileMissing)

于 2012-08-15T18:53:37.380 に答える
0

Parallel.ForEach特定の数の同時インスタンスまで同時にアクション/機能を実行します。これらの反復のそれぞれが本質的に相互に独立していない場合、パフォーマンスは向上しません。また、高価なコンテキストスイッチングと競合が発生するため、パフォーマンスが低下している可能性があります。「データベース呼び出し」を実行し、ファイル操作でそれをシミュレートするとします。各反復で同じリソースを使用する場合(たとえば、データベーステーブルの同じ行、または同じ場所にある同じファイルに書き込もうとする場合)、実際には並列で実行されません。一度に実行されるのは1つだけで、他のユーザーはリソースを取得するために単に「待機」します。コードを複雑にする必要はありません。

各反復で何をしたいのかを詳しく説明していません。しかし、他のプログラマーとこのような状況に遭遇したとき、彼らはほとんどの場合、実際には並行して作業を行っておらず、魔法のようにパフォーマンスを向上させたり、マルチCPUを魔法のように利用したりすることを期待foreachして、単に通過して置き換えました。 Parallel.ForEach/コアプロセッサ。

于 2012-08-15T18:18:12.583 に答える