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);
}

当我遍历我的列表时,myMsgElaspedMilliseconds 似乎是完全相加的——第一个的 ElaspedMilliseconds 可能是 300,但最后一个可能是 50000(50 秒)——这实际上是整个事情的大致时间运行(由另一个计时器测量)。

4

2 回答 2

3

编辑:

哎呀,我也第一次感到困惑。

问题是它看起来只是累加的(累积的),因为 ElapsedTime 值始终仅按递增顺序输出。

所以,如果我有,如下面的演示,按启动顺序:

  • 第一次启动任务的持续时间为 10 秒(10000 毫秒),
  • 第二个任务的持续时间为 8 秒(8 000 毫秒),
  • 3d 任务的持续时间为 6 秒(6 000 毫秒),

然后结果以初始顺序出现在输出中 - 总是按任务持续时间增加的顺序:

  • 第一个输出:3d 启动任务的持续时间(6 秒持续时间)
  • 第二个输出:第二个启动任务的持续时间(持续时间为 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()多线程可能不正确,这意味着一个任务在第一个任务完成后运行,依此类推。每个任务都有自己的计时器,它们都在同一时间开始(或者线程被分配给任务),但是长时间运行的任务抵消了它们。

您永远不会拥有无限的线程池,并且任务处理谁获得线程以及何时获得线程。

关于龙跑:

“它本身并不是一个特定的长度。如果您要生成很多任务,LongRunning 不适合它们。如果您要生成的一两个任务相对于应用程序的生命周期会持续相当长的时间,那么 LongRunning 是要考虑的事情。一般来说,除非你发现你真的需要它,否则不要使用它。在幕后,它会导致使用更多的线程,因为它的目的是让 ThreadPool即使一个任务运行了很长时间,也继续处理工作项;如果该任务在池中的线​​程中运行,则该线程将无法为其他任务提供服务。您d 通常只有在您通过性能测试发现不使用 LongRunning 会导致其他工作处理的长时间延迟时才使用。”——斯蒂芬·图布

于 2013-04-26T14:48:50.763 回答