为什么.NET应用程序中未使用的物理线程数会出现波动?

时间:2014-09-08 02:30:00

标签: c# .net multithreading

我有一个.NET应用程序,我希望它有5个长时间运行的线程,包括主线程。我可以看到确实在代码库中新增了4个线程,我相信在任何地方都没有直接(例如工作项排队/任务)或间接(例如计时器)使用ThreadPool。至少我找不到。

在“性能监视器”下运行应用程序会显示已识别线程的数量保持不变(如我所料),但物理线程数在大约一小时内波动在70到120之间!

有谁知道为什么有这么多未使用的(据我所知)物理线程?为什么这个数字会波动?

我找不到任何可以解释这种行为的文档,所以我最好的猜测是ThreadPool能够平衡自身,以适应不断变化的环境因素,如空闲内存和资源争用,但这里的数字似乎过多。

更新

Microsoft的一位高级支持工程师证实,使用的物理线程计数器肯定只报告当前进程的线程,尽管MSDN中的措辞很奇怪。如果答案表明情况并非如此,则需要指向明确的来源。

4 个答案:

答案 0 :(得分:5)

ThreadPools和GC都创建线程。有一个normal (or "worker") thread pool and an IO threadpool。正常的线程池将分配新线程,因为它认为需要保持线程池响应。它应该立即为每个CPU创建一个线程,并且在此之后可能每秒创建一个线程,直到最小的线程数。有关工作线程池将创建的最小工作线程数,请参阅ThreadPool.GetMinThreads。有关"有效"的数量,请参阅ThreadPool.GetAvailableThreads。工作线程池中的工作线程。如果你有使用工作线程池线程的长时间运行的线程,这将使它认为线程正在使用并分配另一个线程来为将来的请求提供服务。

池中还有一个最大的线程数,因此当线程回收到池中时,池可能会杀掉一些关闭以返回到#,它决定最好。

还有一个终结者帖子。

可能有其他人没有记录或者是您正在使用的图书馆的结果。

更新

我认为问题的一部分是对#34;认可的线程的混淆"和"物理线程"和"未使用的线程"。 已识别的主题是documented as(强调我的)

  

这些线程与相应的托管线程对象相关联。 运行时不会创建这些线程,但它们至少在运行时内运行一次。

物理线程是documented as(强调我的)

  

本机操作系统线程由公共语言运行库创建和拥有,以充当托管线程对象的底层线程

我猜测术语"未使用的线程" @JRoughan指的是"物理线程" - 那些没有被识别的"。这并不意味着他们不被使用,他们只是不在公认的柜台。正如文档所指出的,"物理线程"由运行时创建,我不相信你可以从这些计数器中的任何一个判断一个线程是否被"使用"或"未使用" - 取决于@JRoughan所指的"未使用"。

答案 1 :(得分:2)

这样的事情没有一个简单的答案。您需要在调试器下或使用ETW跟踪进行调查。

使用ETW跟踪,您可以为每个线程创建/销毁获取事件,可选择使用调用堆栈。

CLR本身可以为自己创建线程(例如GC线程,后台GC线程,多核JIT线程),线程池线程,IO线程,计时器线程。还有另一种线程:门线程。

通常,一旦解析了符号,就可以从线程proc的符号名称中判断用法。

对于ETW分析,请使用Microsoft的PerfView。

答案 2 :(得分:1)

您在性能测试中的应用程序是监视一个独立的.net应用程序还是IIS下的应用程序?如果它是一个独立的应用程序,可能你需要添加一些额外的lib /代码来使用性能监视器。它可以创建线程。

您可以使用Sysinternals' Process Explorer用于监视进程中的线程。您可以看到哪个模块启动了线程的方法。

enter image description here

答案 3 :(得分:1)

我们当然只能推测。我自己的赌注是in-process COM servers。当您使用包装COM接口的类(例如目录服务或WMI的类)时,可能会创建它们及其关联的线程。由于它们是由本机代码创建的(即使它包含在dotnet代码中),因此它们不会被识别为托管线程。