【问题标题】:Why does the number of threads reported by WinDbg, Task Manager and VS Debugger differ?为什么WinDbg、任务管理器和VS Debugger报告的线程数不同?
【发布时间】:2011-04-21 15:34:37
【问题描述】:

当我的 .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 启用 11738178:11738778 0042a9f0 0 STA 2 2 1820 004430e0 b220 启用 00000000:00000000 0042a9f0 0 MTA(终结器) 7 5 2c38 055d6330 80a220 启用 00000000:00000000 0042a9f0 0 MTA(线程池完成端口) 8 4 e18 04116900 180b220 启用 1157cdc8:1157e778 0042a9f0 0 MTA(线程池工作者) XXXX 6 0 055f94b0 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 7 0 05649228 9820 启用 00000000:00000000 0042a9f0 0 MTA XXXX 8 0 0567d4f8 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 9 0 05688d68 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX a 0 056fd680 9820 启用 00000000:00000000 0042a9f0 0 MTA XXXX b 0 0575d7f0 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX c 0 056fd250 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX d 0 0572a780 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX e 0 0f082668 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX f 0 0f082a38 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 10 0 0570ca68 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 11 0 0570ce50 9820 启用 00000000:00000000 0042a9f0 0 Ukn 10 12 3fb0 0570d238 180b220 启用 00000000:00000000 0042a9f0 0 MTA(线程池工作者) XXXX 13 0 0570d620 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 14 0 0570da08 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 15 0 0570ddf0 9820 已启用 00000000:00000000 0042a9f0 0 Ukn XXXX 16 0 0570e1d8 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 17 0 0570e5c0 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 18 0 0579e540 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 19 0 0579e928 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 1a 0 0579ed10 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 1b 0 0579f0f8 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 1c 0 0579f4e0 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 1d 0 0579f8c8 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 1e 0 0579fcb0 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 1f 0 057a0098 9820 启用 00000000:00000000 0042a9f0 0 Ukn XXXX 20 0 057a0480 9820 启用 00000000:00000000 0042a9f0 0 Ukn

如果我将 Visual Studio 调试器附加到正在运行的进程,线程窗口会显示 7 个线程。

我有几个问题:

  • 为什么WinDbg说有456个线程而任务管理器说有16个
  • 为什么任务管理器说有 16 个线程而 Visual Studio 调试器说有 7 个
  • !thread 命令显示所有这些死线程都没有 OSID 值。这是否意味着操作系统不再知道它们,而只是布局周围的 .Net 对象?究竟什么是死线程?
  • 我应该担心大量死线程吗?

2010 年 2 月 11 日编辑:以下是有关我的应用的更多信息。我们使用后台线程来轮询服务器并执行其他任务。这些任务每隔几分钟执行一次。我们不使用 .Net 线程池。

2010 年 2 月 18 日编辑:我修复了我们程序中的托管 Thread 对象泄漏(感谢 @highphilosopher)。但是,我关于为什么 WinDbg、任务管理器和 VS 调试器不同意线程数的问题仍未得到解答。谁能解释一下?

2010 年 3 月 1 日编辑:我仍然想知道为什么任务管理器和 Visual Studio 调试器在线程数上不一致。为什么 Visual Studio 会过滤某些线程?它过滤掉什么样的线程?

【问题讨论】:

    标签: .net multithreading windbg


    【解决方案1】:

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

    您的输出中!threads 的输出显示了很多死线程。 id 为XXXX 列出的线程是已终止但尚未收集相应线程对象的线程。 IE。报告的数量远高于实际的线程数。线程计数表明 456 个线程中有 449 个已死。

    我发现线程数很高,如果应用程序一直处于空闲状态,那么它仍然存在很奇怪,但如果没有进一步的信息,很难更具体。

    【讨论】:

    • @Brian:谢谢! Visual Studio 调试器怎么样?为什么它报告 7 个线程而不是任务管理器的 16 个?我预计 VS 会报告 456 个线程或 16 个。
    • @Sly:对不起,我没有解决 VS 的问题。我认为这是一个错字,因为你在谈论 WinDbg。我的猜测是 VS 从显示中过滤了一些 CLR 线程。
    • 如果我在 VS 调试过程中附加到一个进程,我在 WinDbg 中使用 ~ 命令看到的线程比 VS 显示的要多,因此 VS 似乎过滤了其中一些。
    • MCVE 创建 1000 个死线程:stackoverflow.com/a/58963556/480982
    【解决方案2】:

    我的猜测是线程池有一段时间负载很重,然后在负载停止时杀死线程。

    【讨论】:

    • 当我收集内存转储时,我的应用已经闲置了很长时间(几小时)。
    【解决方案3】:

    您所看到的听起来像是在另一个线程上引用了这些线程。由于引用了该线程,因此 GC 无法收集该线程。线程已经完成了它被发送的方法的执行,因此它既不处于睡眠状态,也不处于可运行状态,因此它一定是死的。检查您的代码中的线程集合或类似内容。也许是一个被线程钩住但从未解开的事件?

    【讨论】:

    • 你是对的,我们正在泄漏托管线程对象。我确认使用.Net Memory Profiler。尽管如此,我还是想了解为什么 Visual Studio 和任务管理器在线程数上不一致...
    • 这可能不是您想要的答案,但任务管理器有时会报告一些相当奇怪的值。一个很好的例子是记忆。任务管理器中的内存计数器是您应用程序的工作集。这几乎是无用的信息,因为此内存的一部分与其他进程共享(取决于您的应用程序代码、托管或非托管等)。我尽量避开任务管理器的数字,并尽可能坚持使用 perfmon。
    【解决方案4】:

    线程计数不匹配的另一个原因是任务管理器使用非侵入性技术收集信息。调试器通常,默认情况下使用侵入性附加。

    这意味着,任何时候您调试正在运行的 Windows 应用程序,调试 API 都会将远程线程注入目标应用程序并调用 KERNEL32!DebugBreak,它是一个 int3 (0xcc)操作说明。此时进程中断并停止执行,但是,您现在至少有一个额外的调试线程正在运行。

    【讨论】:

      猜你喜欢
      • 2017-07-14
      • 1970-01-01
      • 2010-11-17
      • 2018-05-03
      • 2011-01-19
      • 2014-11-29
      • 2013-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多