当ThreadPool中的活动线程数大于ThreadPool.GetMinThreads()时启动任务



摘自我之前一个问题的答案(尽管线程池中有可用线程,但Task.Factory.StartNew的启动延迟很大):

"您需要查看的不是MAX工作线程值,而是通过ThreadPool.GetMinThreads()获得的MIN值。最大值是可以活动的绝对最大线程数。最小值是数字以始终保持活动状态。如果您尝试在活动线程数小于最大值(大于最小值)你会看到2秒的延迟。">

所以,我准备了一个测试它的示例代码:

ThreadPool.GetMinThreads()为我的机器返回"8",我在本地机器上运行代码。

我的代码如下:

Task task = null;
int workerThreads = 0;
int completionPortThreads = 0;

ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
Logger.WriteInfo(LogCode.EMPTY_INFO,  workerThreads.ToString());**returns "8"**
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread1");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread1");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread2");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread2");
while (true)
{
DoSomthing();
}
});
;
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread3");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread3");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread4");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread4");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread5");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread5");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread6");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread6");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread7");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread7");
while (true)
{
DoSomthing();
}
});

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread8");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread8");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread10");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread10");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread11");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread11");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread12");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread12");
while (true)
{
DoSomthing();
}
});
Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread13");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread13");
while (true)
{
DoSomthing();
}
});

private void DoSomthing()
{
int j = 1;
for (int i = 0; i < 2000; i++)
{
j = i * i;
}
}

Logger类只使用log4net.dll。因此,ThreadPool.GetMinThreads()为我的机器返回8。最小值是始终保持活动状态的数字。如果您试图在活动线程数小于max(大于min)时启动线程,则会看到2秒的延迟

所以,对于线程号9等等:

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");
task = Task.Factory.StartNew(() =>
{
Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");
while (true)
{
DoSomthing();
}
});

我预计之间会有2秒的延迟

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");

Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");

因为我试图在活动线程数大于min(大于8)时启动线程(从线程池)。实际结果是只有几毫秒的延迟(不到半秒)。

为什么没有至少2秒的延迟?我的意思是,我的线程池中的所有活动线程都很忙,而对于线程9,它需要分配另一个线程,所以应该有一个延迟。

在编写了这个示例应用程序之后,我不确定我之前的问题中的场景。

这里有几个问题。

  • 首先是,您使用的是可能不应该使用的Task.Factory.StartNew,在大多数情况下,您可能应该使用更现代的Task.Run,有很多关于这方面的问题和博客。

  • 其次,您引用的问题引用的文档比当前框架旧。文档已更改。它过去在创建线程时规定一毫秒的延迟。

  • 第三,任务不是线程。

我对此的理解是任务调度程序(取决于你使用的)使用启发式来确定它是否想在每个类别中给你一个线程,并且没有任意的毫秒延迟。

文件目前所说的是。

线程池类

线程池提供新的工作线程或I/O完成线程按需提供,直到达到每个类别的最小值。当达到最小值时,线程池可以在该类别,或者等待一些任务完成。

事实是,如果您依赖任务调度程序的典型行为以特定的速度分配线程,那么您肯定做错了什么。这是一个实现细节,可能会因版本而异。最多可以增加最少的线程数量,但任务调度器的工作是在很大程度上将您从这个细节级别抽象出来。它旨在为你做最好的事情。

如果你需要一定数量的线程,要么构建你自己的任务调度程序,要么创建你自己的线程并一起跳过任务调度程序

好吧,查看MSDN的ThreadPool文档,我没有发现任何与达到大于Minimum值的值时延迟2秒有关的信息,我认为这不是准确的答案,因为延迟取决于与硬件和操作系统相关的许多因素,指定了以下内容:

默认情况下,最小线程数设置为系统上的处理器数。当达到最小值时,线程池可以在该类别中创建其他线程,或者等待一些任务完成。从.NET Framework 4开始,线程池创建和销毁线程以优化吞吐量,吞吐量定义为每单位时间完成的任务数。线程太少可能无法最佳利用可用资源,而线程太多可能会增加资源争用。

GetMinThreads的文档如下:

在切换到管理线程创建和销毁的算法之前,检索线程池在发出新请求时按需创建的最小线程数

文档中没有详细说明切换算法,那么这2秒延迟的原因是什么,是在他的机器上测试吗?

然而,如果你需要更多关于这方面的信息,.NET framework Source是公开的,你可以深入检查算法,但由于硬件和操作系统的依赖性因素,你不会获得延迟的静态数字。

最新更新