为什么WinDbg、任务管理器和VS调试器报告的线程数量不同?

15

我的 .Net 3.5 应用程序在运行时,Windows 任务管理器显示我的应用程序有16个线程。我为该进程收集了一个内存转储,并使用 WinDbg/SOS 打开了它。

运行 !threads 命令会显示我有:

ThreadCount: 456
UnstartedThread: 0
BackgroundThread: 6
PendingThread: 0
DeadThread: 449
Hosted Runtime: no
以下是 !threads 命令的前几行输出:
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
   0    1 2848 004366a8      6020 Enabled  11738178:11738778 0042a9f0     0 STA
   2    2 1820 004430e0      b220 Enabled  00000000:00000000 0042a9f0     0 MTA (Finalizer)
   7    5 2c38 055d6330    80a220 Enabled  00000000:00000000 0042a9f0     0 MTA (Threadpool Completion Port)
   8    4  e18 04116900   180b220 Enabled  1157cdc8:1157e778 0042a9f0     0 MTA (Threadpool Worker)
XXXX    6    0 055f94b0      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    7    0 05649228      9820 Enabled  00000000:00000000 0042a9f0     0 MTA
XXXX    8    0 0567d4f8      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    9    0 05688d68      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    a    0 056fd680      9820 Enabled  00000000:00000000 0042a9f0     0 MTA
XXXX    b    0 0575d7f0      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    c    0 056fd250      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    d    0 0572a780      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    e    0 0f082668      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX    f    0 0f082a38      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   10    0 0570ca68      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   11    0 0570ce50      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
  10   12 3fb0 0570d238   180b220 Enabled  00000000:00000000 0042a9f0     0 MTA (Threadpool Worker)
XXXX   13    0 0570d620      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   14    0 0570da08      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   15    0 0570ddf0      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   16    0 0570e1d8      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   17    0 0570e5c0      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   18    0 0579e540      9820 Enabled  00000000:00000000 0042a9f0     0 Ukn
XXXX   19    0 0579e928      9820 Enabled  00000000:00000000 0042a9f0    
4个回答

11

任务管理器报告了你的进程的总线程数,而!threads报告的是托管线程数量。如果在WinDbg中使用~命令,则可以看到该进程的所有线程。

!threads命令的输出显示了很多死亡线程。用XXXX作为ID列出的线程已经终止,但相应的线程对象尚未被收集。也就是说,报告的线程数比实际线程数要高得多。线程计数数字表明,456个线程中有449个线程已经死亡。

我认为线程数量很高,如果应用程序处于空闲状态,它们仍然存在是奇怪的,但没有更多的信息,很难详细说明。


@Brian:谢谢!那Visual Studio调试器怎么样?为什么它报告7个线程而不是任务管理器的16个?我期望VS要么报告456个线程,要么报告16个。 - Sylvain
@Sly:抱歉我没有回答关于VS的问题。我认为那是一个打字错误,因为你在谈论WinDbg。我的猜测是,VS从显示中过滤了一些CLR线程。 - Brian Rasmussen
如果我在 VS 调试进程时附加到该进程,使用 WinDbg 的 ~ 命令可以看到比 VS 显示更多的线程,因此 VS 似乎会过滤其中一些。 - Brian Rasmussen
MCVE创建了1000个死线程:https://dev59.com/dbfna4cB1Zd3GeqPvpl4#58963556 - Thomas Weller

1

我猜测线程池经历了一段时间的大负载,然后在负载减轻时杀死了这些线程。


当我收集内存转储时,我的应用程序已经闲置了很长时间(数小时)。 - Sylvain

1

你所看到的情况似乎是你在不同的线程上引用了这些线程。由于线程被引用,因此无法被GC回收。该线程已完成其被发送执行的方法的执行,因此它既不处于睡眠状态也不处于可运行状态,因此必须是死亡状态。检查你的代码是否有线程集合或类似的东西。也许是一个事件被线程挂钩,但从未卸下?


你说得对,我们正在泄漏托管的线程对象。使用 .Net Memory Profiler 我确认了这一点。但是,我仍然想知道为什么 Visual Studio 和任务管理器在线程数量上无法达成一致... - Sylvain
这可能不是你想要的答案,但任务管理器有时会报告一些相当奇怪的值。一个很好的例子是内存。任务管理器中的内存计数器是应用程序的工作集。这几乎是无用的信息,因为这部分内存是与其他进程共享的(取决于你的应用程序代码、托管或非托管等)。我尽量避免使用任务管理器来获取数据,并尽可能使用 Perfmon。 - SamuelWarren

0
另一个导致线程数不匹配的原因是任务管理器使用的是非侵入式技术来收集信息。调试器通常默认使用侵入性的附加方式。
这意味着,每当你调试正在运行的Windows应用程序时,调试API会将一个远程线程注入到目标应用程序中,并调用KERNEL32!DebugBreak函数,它是一条int3(0xcc)指令。此时,进程会中断并停止执行,但是你现在至少有一个额外的调试线程在运行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接