5

我有一个基于 3.5 构建的多线程 wpf 应用程序。当我通过 Process Explorer 查看正在运行的线程时,我看到 8 个线程都具有相同的起始地址 ntdll.dll!RtlUserThreadStart,并且所有 8 个线程的 CPU 值都在 3-6+ 之间,并且具有较高的 Cycles Delta。我无法弄清楚这些线程在做什么。它总是相同的线程。它永远不会在应用程序的同一实例中发生变化。当我同时调试我的应用程序并暂停调试器时,所有这些线程都为堆栈显示一行 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

作为该项目的附加说明。我的电脑是一个4核的单CPU。当我们在具有 4 个内核的双 CPU 上运行相同的应用程序时,我们会看到线程数从 8 个变为 16 个。

4

2 回答 2

6

您的问题被严重记录不足,但合理的猜测是您似乎使用了 PPL 库。它保留了一个线程池来完成并行作业。毫无疑问,您会看到高 CPU 周期计数,因为这些线程确实在执行您要求它们执行的工作。

与线程池的典型情况一样,PPL 保留这些线程以供下一项工作执行,这就是您看到它们在 WaitForWork() 上等待的原因。由于缺少调试符号,本机堆栈跟踪是垃圾。RtlUserThreadStart 是一个 Windows 函数,你总是会在非托管堆栈跟踪中看到它,这就是线程启动的方式。

这都是完全正常的。唯一值得注意的其他信息是微软员工发布的这个答案:

并发运行时缓存线程以供以后重用。它们仅在所有并发运行时调度程序都已关闭时才被释放。(通常,进程中只有一个默认调度程序)。当排队工作的所有外部线程都退出时,调度程序将关闭。因此,如果主线程调度工作(通过从 main() 调用 parallel_for 说),那么默认调度程序将仅在进程关闭时被删除。

缓存线程数有上限。它大约是机器上内核数量的 4 倍(尽管还有一些其他因素会影响阈值,例如调度程序策略中的堆栈大小选项)。

于 2012-06-28T12:59:42.247 回答
3

我找出了导致这些处于等待状态的线程上 CPU 利用率高的原因。我还不知道为什么会这样。当我们的应用程序是 .NET 3.5 应用程序时,有人在这里找到并利用了一个可用的线程程序集,该程序集有人向后移植或来自 .NET 4.0/4.5 的东西用于 3.5。这显然在 Parallel.ForEach 调用或其他方面存在缺陷。当我调用这个调用时,我最终会在循环之后等待这些线程等待,消耗 CPU。我们与 Microsoft 确认这些线程实际上只是在等待。现在我们是 4.0,我已经切换到 4.0 可用的任务库,问题已经消失。当我有机会查看是否可以提供发生这种情况的具体原因时,我将尝试调试到库中。

于 2013-09-18T12:32:11.243 回答