C# 为什么任务并行库有一个';隐藏的';在特定条件下安排任务需要1秒超时吗?

C# 为什么任务并行库有一个';隐藏的';在特定条件下安排任务需要1秒超时吗?,c#,concurrency,task-parallel-library,C#,Concurrency,Task Parallel Library,我的笔记本电脑有两个逻辑处理器,我偶然发现了这样一个场景:如果我安排了两个任务,时间超过1秒,但没有指定它们是长时间运行的,那么后续任务将在1秒后启动。是否可以更改此超时 我知道正常的任务应该是短时间运行的——如果可能的话,短于一秒钟——我只是想知道我是否看到了硬编码的TPL行为,或者我是否能够以任何方式影响这种行为,而不是指定任务长时间运行 此控制台应用程序方法应演示具有任意数量处理器的计算机的行为: static void Main(string[] args) { var time

我的笔记本电脑有两个逻辑处理器,我偶然发现了这样一个场景:如果我安排了两个任务,时间超过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();
}
台词:

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

指示存在1秒超时或类似性质的时间,允许较短的运行任务在“粗鲁”任务的基础上进行调度。这就是我要问的。

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


如果要更改调度行为,可能需要查看。

这是线程池调度程序的标准行为。它试图使活动线程的数量与内核的数量相等。但是,如果您的任务执行大量的阻塞而不是运行,则无法很好地完成任务。睡在你的箱子里。每秒两次,它允许另一个线程运行,以尝试处理积压工作。看起来你有一个双核cpu


正确的解决方法是使用TaskCreationOptions.LongRunning,以便计划程序使用常规线程而不是线程池线程。一种不正确的解决方法是使用ThreadPool.SetMinThreads。但是您可能应该专注于在任务中执行实际工作,Sleep()并不是一个很好的模拟方法。

问题是调度程序在尝试确定任务是否长期运行时,启动新任务需要一段时间。您可以告诉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));
}

请发表一篇关于不良行为的描述。@AdamRobinson嗨,Adam,第一段描述了我看到的行为-我不确定是否应该称之为“不良行为”,但我想知道我是否可以以任何方式影响这种行为,或者我是否需要调整我的期望和使用TPLSorry的方法,我指的是一些示例输出,演示您所描述的内容。我不太清楚您描述的是什么,但看到您的控制台输出可能会更清楚。@AdamRobinson同样,我也非常乐意为您提供如何更有效地提出这个问题的建议。@AdamRobinson好的,我添加了输出-谢谢!
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