【问题标题】:Why does the Task Parallel Library have a 'hidden' 1 second timeout for scheduling tasks under certain conditions?为什么任务并行库在某些条件下调度任务有一个“隐藏”的 1 秒超时?
【发布时间】:2012-02-29 13:01:38
【问题描述】:

我的笔记本电脑有 2 个逻辑处理器,我偶然发现了这样一种情况:如果我安排了 2 个耗时超过 1 秒的任务而不指定它们长时间运行,则后续任务会在 1 秒后启动。这个超时时间可以改吗?

我知道正常的任务应该是短时间运行的——如果可能的话,比一秒钟短得多——我只是想知道我看到的是硬编码的 TPL 行为,或者我是否可以以任何方式影响这种行为,而不是长时间指定任务——正在运行。

此控制台应用程序方法应演示具有任意数量处理器的机器的行为:

static void Main(string[] args)
{
    var timer = new Stopwatch();
    timer.Start();

    int numberOfTasks = Environment.ProcessorCount;

    var rudeTasks = new List<Task>();
    var shortTasks = new List<Task>();

    for (int index = 0; index < numberOfTasks; index++)
    {
        int capturedIndex = index;
        rudeTasks.Add(Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Starting rude task {0} at {1}ms", capturedIndex, timer.ElapsedMilliseconds);
            Thread.Sleep(5000);
        }));
    }

    for (int index = 0; index < numberOfTasks; index++)
    {
        int capturedIndex = index;
        shortTasks.Add(Task.Factory.StartNew(() =>
        {
            Console.WriteLine("Short-running task {0} running at {1}ms", capturedIndex, timer.ElapsedMilliseconds);
        }));
    }

    Task.WaitAll(shortTasks.ToArray());
    Console.WriteLine("Finished waiting for short tasks at {0}ms", timer.ElapsedMilliseconds);

    Task.WaitAll(rudeTasks.ToArray());
    Console.WriteLine("Finished waiting for rude tasks at {0}ms", timer.ElapsedMilliseconds);

    Console.ReadLine();
}

这是应用程序在我的 2 proc 笔记本电脑上的输出:

Starting rude task 0 at 2ms
Starting rude task 1 at 2ms
Short-running task 0 running at 1002ms
Short-running task 1 running at 1002ms
Finished waiting for short tasks at 1002ms
Finished waiting for rude tasks at 5004ms

Press any key to continue . . .

线条:

Short-running task 0 running at 1002ms
Short-running task 1 running at 1002ms

表示有 1 秒超时或类似性质的东西,允许较短运行的任务被安排在“粗鲁”任务之上。这就是我要问的问题。

【问题讨论】:

  • 请发布对不当行为的描述。
  • @AdamRobinson 嗨亚当,第一段描述了我所看到的行为 - 我不确定是否应该称其为“不当行为”,但我想知道我是否可以以任何方式影响这种行为,或者如果我需要调整使用 TPL 的期望和方法
  • 对不起,我的意思是一些示例输出来演示您所描述的内容。我不太清楚你在描述什么,但看到你的控制台输出可能会更清楚。
  • @AdamRobinson 另外,如果我能更有效地提出这个问题,我会非常高兴。
  • @AdamRobinson 好的,我添加了输出 - 谢谢!

标签: c# concurrency task-parallel-library


【解决方案1】:

您看到的行为并非特定于 TPL,而是特定于 TPL's default scheduler。调度程序正在尝试增加线程的数量,这样正在运行的两个线程就不会“占用”CPU 并阻塞其他线程。如果正在运行的两个任务本身启动并等待任务,这也有助于避免死锁情况。

如果您想更改调度行为,您可能需要查看implementing your own TaskScheduler

【讨论】:

    【解决方案2】:

    这是线程池调度程序的标准行为。它试图保持活动线程的数量等于核心的数量。但是当你的任务做很多阻塞而不是运行时,就不能很好地完成这项工作。在你的情况下睡觉。它每秒两次允许另一个线程运行以尝试处理积压工作。好像你有一个双核cpu。

    正确的解决方法是使用 TaskCreationOptions.LongRunning,以便调度程序使用常规线程而不是线程池线程。一个不恰当的解决方法是使用 ThreadPool.SetMinThreads。但是你或许应该专注于在你的任务中做真正的工作,Sleep() 并不是一个很好的模拟。

    【讨论】:

      【解决方案3】:

      问题是调度程序需要一段时间来启动新任务,因为它会尝试确定任务是否长时间运行。您可以告诉 TPL 任务长时间运行作为任务的参数:

      for (int index = 0; index < numberOfTasks; index++)
      {
          int capturedIndex = index;
          rudeTasks.Add(Task.Factory.StartNew(() =>
          {
              Console.WriteLine("Starting rude task {0} at {1}ms", capturedIndex, timer.ElapsedMilliseconds);
              Thread.Sleep(3000);
          }, TaskCreationOptions.LongRunning));
      }
      

      导致:

      Starting rude task 0 at 11ms
      Starting rude task 1 at 13ms
      Starting rude task 2 at 15ms
      Starting rude task 3 at 19ms
      Short-running task 0 running at 45ms
      Short-running task 1 running at 45ms
      Short-running task 2 running at 45ms
      Short-running task 3 running at 45ms
      Finished waiting for short tasks at 46ms
      Finished waiting for rude tasks at 3019ms
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多