6

私はループで実行しており、次の方法でタスクを開始しています。

var iResult = new List<Task>();
foreach(var i in myCollection)
{
    var task = Task.Factory.StartNew(() =>
                    DoSomething(), TaskCreationOptions.LongRunning);
    task.ContinueWith(m => myResponseHandler(m.Result));
    iResult.Add(task);
}

私のDoSomething()メソッド内には、タイマーがあります。

public static myMsg DoSomething()
{
    var timer = System.Diagnostics.Stopwatch.StartNew();
    DoLongRunningTask(); //If it matters this hits a REST endpoint (https)
    timer.Stop();

    return new myMsg(timer.ElaspedMilliseconds);
}

のリストを繰り返し処理するとmyMsg、EaspedMilliseconds は完全に加算されるように見えます。最初の EaspedMilliseconds は 300 かもしれませんが、最後の EaspedMilliseconds は 50000 (50 秒) になる可能性があります。 run (別のタイマーで測定)。

4

2 に答える 2

3

編集:

おっと、私も最初戸惑いました。

問題は、ElapsedTime 値が常に昇順でのみ出力されるため、相加的 (累積的) にしか見えないことです。

したがって、以下のデモのように、起動する順序があるとします。

  • 最初に起動されたタスクの持続時間 10 秒 (10000 ミリ秒)、
  • 2 番目のタスクの持続時間 8 秒 (8 000 ミリ秒)、
  • 3D タスクの持続時間 6 秒 (6 000 ミリ秒)、

その後、結果は最初の順序から外れて出力に表示されます-常にタスクの期間が長くなる順序で:

  • 最初の出力: 3D 起動タスクの期間 (6 秒の期間)
  • 出力の 2 番目: 2 番目に起動されたタスクの期間 (8 秒の期間)
  • 出力の 3d (最後): 最初に起動されたタスクの期間 (10 秒の期間)

以下は、コンソール アプリからの出力です。

from DoSomething  6043  
from main  6043  
from DoSomething  8057  
from main  8057  
from DoSomething  10058
from main  10058

そして、その理由は明らかです - より速いタスクは常に終了し、より長い (より時間がかかる) タスクの前に出力されるためです。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      var iResult = new List<Task>();
      for (int i=5; i>2; i--)
      {
        int load = i;
        var task = Task.Factory.StartNew(() =>
                        DoSomething(load), TaskCreationOptions.LongRunning);
        //following commented lines do NOT change the behavior in question
        task.ContinueWith(m => Console.WriteLine("from main  "+m.Result));
        //iResult.Add(task);
      }
      Console.ReadLine();
    }

   //public static myMsg DoSomething()
    public static long DoSomething(int load)
    {
      Stopwatch timer = System.Diagnostics.Stopwatch.StartNew();

      //usage of either prev or following 2 lines produce the same results 
      //Stopwatch timer = new Stopwatch();  //instead of prev .StartNew();
      //timer.Start();// instead of prev .StartNew();

      Console.WriteLine("***Before calling  DoLongRunningTask()   " 
               + timer.ElapsedMilliseconds);
      Console.WriteLine("GetHashCode  "+timer.GetHashCode());

      DoLongRunningTask(load); 
      timer.Stop();

      long elapsed = timer.ElapsedMilliseconds;
      Console.WriteLine("from DoSomething  "+ elapsed);

      return elapsed;//return new myMsg(timer.ElaspedMilliseconds);
    }

    public static void DoLongRunningTask(int load)
    {
      Thread.Sleep(2000*load);
      /*******************  another variant of calculation intensive loading
             load = load;
             double result = 0;
             for (int i = 1; i < load*100000; i++)
                   result += Math.Exp(Math.Log(i) );
       */
    }
  }
}
于 2013-04-28T02:44:28.067 に答える
2

発生する可能性が最も高いのは、DoLongRunningTask()適切にマルチスレッド化されていない可能性があることです。これは、最初のタスクが完了した後に 1 つのタスクが実行されることを意味します。各タスクには独自のタイマーがあり、それらはすべてほぼ同時に (またはスレッドがタスクに割り当てられたときに) 開始されますが、長時間実行されるタスクはそれらすべてを相殺します。

無制限のスレッド プールや、誰がいつスレッドを取得するかを処理するタスクはありません。

ロングランニングについて:

「それ自体は特定の長さではありません。多くのタスクを生成している場合、LongRunning は適切ではありません。アプリケーションの寿命に比べてかなりの時間持続する 1 つまたは 2 つのタスクを生成している場合、 LongRunning は考慮すべきものです. 一般に, 本当に必要であることが判明しない限り使用しないでください. 内部的には, より多くのスレッドが使用されることになります. 1 つのタスクが長時間実行されていても、作業項目の処理を続行します。そのタスクがプールのスレッドで実行されている場合、そのスレッドは他のタスクを処理できません。通常、LongRunning を使用しないと他の作業の処理に長い遅延が発生することがパフォーマンス テストでわかった場合にのみ、LongRunning を使用します。"- スティーブン・トゥーブ

于 2013-04-26T14:48:50.763 に答える