在Process Explorer中调试RtlUserThreadStart

7

我有一个基于3.5构建的多线程wpf应用。当我通过进程资源管理器查看运行中的线程时,我看到了8个线程,它们的起始地址都相同,即ntdll.dll!RtlUserThreadStart,并且所有八个线程的CPU值都在3-6+之间,并且具有高循环间隔。我无法确定这些线程正在做什么。它们总是相同的线程,同一应用程序实例内从不变化。同时进行应用程序调试并暂停调试器时,所有这些线程都显示堆栈的单个行,要么是System.Threading.ConcurrencyScheduler.Scheduler.WaitForWork(),要么是System.Threading.Monitor.Wait()。

我启用了Visual Studio的符号文件,并在那些线程上看到了以下堆栈:

System.Threading.Monitor.Wait() Normal
mscorlib.dll!System.Threading.Monitor.Wait(object obj, int millisecondsTimeout) + 0x19     bytes
System.Threading.dll!System.Threading.ConcurrencyScheduler.Scheduler.WaitForWork() + 0xd0 bytes  
System.Threading.dll!System.Threading.ConcurrencyScheduler.InternalContext.Dispatch() + 0x74a bytes
System.Threading.dll!System.Threading.ConcurrencyScheduler.ThreadInternalContext.ThreadStartBridge(System.IntPtr dummy) + 0x9f bytes     

当我查看进程监视器中提供的线程堆栈时,以下是一些示例:

0  ntoskrnl.exe!KeWaitForMultipleObjects+0xc0a
1  ntoskrnl.exe!KeAcquireSpinLockAtDpcLevel+0x732
2  ntoskrnl.exe!KeWaitForSingleObject+0x19f
3  ntoskrnl.exe!_misaligned_access+0xba4
4  ntoskrnl.exe!_misaligned_access+0x1821
5  ntoskrnl.exe!_misaligned_access+0x1a97
6  mscorwks.dll!InitializeFusion+0x990b
7  mscorwks.dll!DeleteShadowCache+0x31ef

或者:

0  ntoskrnl.exe!KeWaitForMultipleObjects+0xc0a
1  ntoskrnl.exe!KeAcquireSpinLockAtDpcLevel+0x732
2  ntoskrnl.exe!KeWaitForSingleObject+0x19f
3  ntoskrnl.exe!_misaligned_access+0xba4
4  ntoskrnl.exe!_misaligned_access+0x1821
5  ntoskrnl.exe!KeAcquireSpinLockAtDpcLevel+0x93d
6  ntoskrnl.exe!KeWaitForMultipleObjects+0x26a
7  ntoskrnl.exe!NtWaitForSingleObject+0x41f
8  ntoskrnl.exe!NtWaitForSingleObject+0x78e
9  ntoskrnl.exe!KeSynchronizeExecution+0x3a23
10 ntdll.dll!ZwWaitForMultipleObjects+0xa
11 KERNELBASE.dll!GetCurrentProcess+0x40
12 KERNEL32.dll!WaitForMultipleObjectsEx+0xb3
13 mscorwks.dll!CreateApplicationContext+0x10499
14 mscorwks.dll!CreateApplicationContext+0xbc41
15 mscorwks.dll!StrongNameFreeBuffer+0xc54d
16 mscorwks.dll!StrongNameFreeBuffer+0x2ac48
17 mscorwks.dll!StrongNameTokenFromPublicKey+0x1a5ea
18 mscorwks.dll!CopyPDBs+0x17362
19 mscorwks.dll!CorExitProcess+0x3dc9
20 mscorwks.dll!TranslateSecurityAttributes+0x547f
21 mscorlib.ni.dll+0x8e6bc9

关于这个问题的补充说明。我的计算机是单个CPU,有4个内核。当我们在一个双CPU,4个内核的计算机上运行相同的应用程序时,我们会看到线程数量从8个增加到16个。


1
如果您想要调试它,那么您需要获取更好的调试符号。任何出现偏移量大于0x100的符号基本上都是垃圾,无法告诉您实际发生了什么。目前,您只能看到导出函数。启用Microsoft符号服务器以获取更好的PDB文件。 - Hans Passant
1
我在启用符号服务器时,添加了Visual Studio提供的堆栈。但我不知道如何增强Process Explorer中提供的堆栈。 - Ben
顺便提一下:如果Process Explorer没有完全权限查看图像,则似乎会显示“RtlUserThreadStart”。例如,当MSI安装程序正在运行时,我注意到了这一点。当您使用“文件->显示所有进程信息”设置时,它将解析堆栈到更深的元素。 (您还会注意到,“堆栈跟踪”按钮在没有提升特权的情况下打印错误。) - eckes
2个回答

6
您的问题文档不够详细,但可以猜测您似乎在使用PPL库。该库维护一组线程以完成并行作业。您无疑会看到高CPU周期计数,因为这些线程确实在执行您要求它们执行的工作。
与线程池一样,PPL会保留这些线程以执行下一个作业,这就是为什么您会看到它们在等待WaitForWork()的原因。由于缺乏调试符号,本机堆栈跟踪是垃圾。RtlUserThreadStart是Windows函数,在未经处理的堆栈跟踪中始终会出现,这是线程启动的方式。
这一切都是完全正常的。唯一值得注意的其他信息是Microsoft员工发布的此答案
并发运行时缓存线程以供以后重用。只有当所有并发运行时调度程序关闭时,它们才会被释放。(通常,进程中只有一个默认调度程序)。当排队工作的所有外部线程退出时,调度程序将被关闭。因此,如果主线程安排了工作(例如从main()调用parallel_for),则默认调度程序仅在进程关闭时被删除。
缓存线程的数量有上限。它大约是机器上核心数的4倍(尽管还有一些其他因素影响阈值,如调度程序策略中的堆栈大小选项)。

我理解维护这些线程以备后用的概念。但我不明白或不知道如何调试,为什么它们在等待工作时会消耗这么多的CPU资源?我看到每个线程都在消耗3-6%的CPU资源。 - Ben
因为这些线程实际上正在工作? 你编写了一个“多线程wpf应用程序”,它们应该在工作。 Process Explorer有时无法帮助捕获它们执行工作的情况。 当它进行快照时,它正在使用您计算机的一个核心,这使得其中一个工作线程的执行更加不太可能。 添加仪表代码或使用并发分析器以获得更好的洞察力。 - Hans Passant

3

我找出了导致处于等待状态的线程高CPU利用率的原因,但是我还不知道为什么会这样。当我们的应用程序是.NET 3.5应用程序时,这里的某个人发现并利用了一个可用的线程程序集,该程序集是从.NET 4.0/4.5中回溯或提取出来供3.5使用的。这显然在Parallel.ForEach调用中存在缺陷或其他问题。当我调用此调用后,循环结束后这些线程就会空闲等待,消耗CPU。我们与Microsoft确认这些线程实际上只是在等待。现在我们使用的是4.0,并且我已经切换到4.0可用的任务库,问题已经解决了。我将尝试在有机会时调试该库,看是否能够提供导致问题发生的具体原因。


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