代码:
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是做什么的吗?有人可以澄清一下发生了什么吗?
答案 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决定做一些它认为有益的事情。