多个任务变慢

时间:2011-09-12 20:56:08

标签: c# multithreading

代码:

static void DoIt(string name)
{
    Console.WriteLine("Hello {0} | {1}", name, Thread.CurrentThread.ManagedThreadID);
    Thread.Sleep(5000);
    Console.WriteLine("Bye {0} | {1}", name, Thread.CurrentThread.ManagedThreadID);
}

static void Main()
{
    Task.Factory.StartNew(() => DoIt("One"));
    Task.Factory.StartNew(() => DoIt("Two"));
    Task.Factory.StartNew(() => DoIt("Three"));
    Task.Factory.StartNew(() => DoIt("Four"));
    Task.Factory.StartNew(() => DoIt("Five"));
    Task.Factory.StartNew(() => DoIt("Six"));
    Task.Factory.StartNew(() => DoIt("Seven"));
    Task.Factory.StartNew(() => DoIt("Eight"));
    Task.Factory.StartNew(() => DoIt("Nine"));
    Task.Factory.StartNew(() => DoIt("Ten"));

    Console.ReadKey();
}

为什么它可以立即精确启动前3个任务,但是任务4启动需要5-10秒,在任务4启动后,任务5启动前需要5-10秒,依此类推。 GC是做什么的吗?有人可以澄清一下发生了什么吗?

3 个答案:

答案 0 :(得分:18)

  

为什么它可以立即精确启动前3个任务,但是任务4启动需要5-10秒,在任务4启动后,任务5启动前需要5-10秒,依此类推。 GC是做什么的吗?有人可以澄清一下发生了什么吗?

默认情况下,第一次运行此命令时,将使用最小工作线程数分配ThreadPool。在安排了前4个任务之后,线程池将“加速”以便随着时间的推移处理更多,这就是你看到延迟的原因。

在我的系统(有8个核心)上,前8个是即时的,然后接下来的两个在一秒后启动。

在您的情况下,如果您运行测试两次,时间,则线程将立即启动。这是因为,在第一次运行之后,ThreadPool应该有足够的工作人员立即安排它。

尝试以下操作以查看此行为。如果您将SetMinThreads呼叫保留在原地,则会立即安排这些呼叫。如果你注释掉它,你会看到,第一次,它需要一段时间,但第二次通过(假设你等待线程完成),线程将立即运行。

static void DoIt(string name)
{
    Console.WriteLine("Hello {0} | {1} - {2}", name, Thread.CurrentThread.ManagedThreadId, DateTime.Now);
    Thread.Sleep(5000);
    Console.WriteLine("Bye {0} | {1} - {2}", name, Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}

static void Main()
{
    int workerThreads, complete;
    ThreadPool.GetMinThreads(out workerThreads, out complete);

    Console.WriteLine(workerThreads);

    // Comment out this line to see the difference...
    // WIth this commented out, the second iteration will be immediate
    ThreadPool.SetMinThreads(100, complete);

    Action run = () =>
        {
            for (int i = 0; i < 20; ++i)
            {
                int tmp = i;
                Task.Factory.StartNew(() => DoIt(tmp.ToString()));
            }
        };

    run();
    Console.WriteLine("Press a key to run again...");
    Console.ReadKey();

    run();

    Console.WriteLine("Press a key to exit...");
    Console.ReadKey();
}

请注意,这种行为实际上与整个TPL没什么关系 - 它更多是默认的TaskScheduler,它只是将任务传递给ThreadPool。例如,如果您在LongRunning调用中使用StartNew()提示设置这些线程,则它们都会立即启动(因为默认调度程序将设置一个新的专用线程并立即执行它)。

答案 1 :(得分:6)

任务没有减慢,它们被任务并行库排队。 CLR知道您的计算机上有多少逻辑核心可用; TPL 线程池使用此信息来确定要提供的工作线程数。在您的情况下,您可能有四个逻辑核心;为主线程(正在运行Main())取一个核心(三个线程池工作者)仍然执行任务。

答案 2 :(得分:0)

TPL包含非常复杂的算法,可以适应工作负载。您正在体验4核机器的声音 - 在创建如此多线程的情况下,TPL决定做一些它认为有益的事情。