.Net ThreadPool 线程是如何创建的?
How does a .Net ThreadPool thread created?
我想了解 TheadPool 的工作原理。像这样的简单控制台应用程序:
public static void Main()
{
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
Console.WriteLine($"thread.ThreadState: {thread.ThreadState}");
}
Console.ReadLine();
}
输出为:
thread.ThreadState: Running
thread.ThreadState: Wait
thread.ThreadState: Wait
thread.ThreadState: Wait
thread.ThreadState: Wait
所以有5个线程,1个运行,4个等待。我的问题是:
这些线程来自哪里? 我猜这 4 个等待线程是 ThreadPool 线程。它们必须在进入我的主要方法之前创建。 你能指出创建这些线程的 .Net 源代码吗?
我知道我们可以 post 使用 ThreadPool.QueueUserWorkItem 将任务发送到线程池,但是线程池如何获取 task/delegate?我想有一些类似 WinForm UI 线程的消息泵之类的东西,是否有一个后台线程不断检查是否有任何新任务?也可以看看源码吗?
编辑:
感谢 Thomas,所以我关于 ThreadPool 的 assumptions/imaginations 是完全错误的。我会用更合适的例子打开另一个问题。
基础知识
总而言之,您的所有问题都非常深入,但您目前似乎不具备理解所有这些的知识。
首先,您的代码显示的是所有线程的列表,而不仅仅是 .NET 线程。知道这一点很重要。
Where do these threads come from?
一般可以来自
- .NET
- 加载到 .NET 应用程序中的本机代码
- 挂钩
- 调试器
- ...可能更多。
您可以使用调试器并查看调用堆栈以了解这些线程的作用(稍后将介绍)。
Can you point me to the .Net source code that creating those threads?
当 运行 .EXE 文件时,操作系统会创建一个线程。您不会在 .NET 框架中找到它。
应该可以在 .NET 源代码中找到 Finalizer 线程和 Threadpool 线程的位置。
我认为一段相关代码在 Threadpool.cs 的第 1776 行到 1780 行:
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
internal static extern bool RequestWorkerThread();
基本上 extern
说它不是在 C# 中实现的,而是在一些本机代码中实现的。
how does the ThreadPool pick up a task/delegate?
所有任务都进入队列。再看Threadpool.cs(写作时第71行):
internal sealed class ThreadPoolWorkQueue
它有一个Add()
方法和一个Remove()
方法。
is there one background thread constantly checking if there is any new task?
没有。您的代码将项目插入队列,线程池工作线程从队列中取出项目。
can I see the source code?
与上述相同的位置。粘贴在这里太多了。
正在调试
请注意,您需要一个还可以显示本机线程的调试器。另请注意,调试器会创建一个额外的线程以进入应用程序。
可以使用WinDbg的~
命令查看线程列表:
0:004> ~
0 Id: 223c.650 Suspend: 1 Teb: fffdd000 Unfrozen
1 Id: 223c.2494 Suspend: 1 Teb: fffda000 Unfrozen
2 Id: 223c.13b4 Suspend: 1 Teb: fffd7000 Unfrozen
3 Id: 223c.1edc Suspend: 1 Teb: fffaf000 Unfrozen
. 4 Id: 223c.230c Suspend: 1 Teb: fffac000 Unfrozen
你可以看到所有线程的调用堆栈使用~*k
:
0:004> ~*k
0 Id: 223c.650 Suspend: 1 Teb: fffdd000 Unfrozen
# ChildEBP RetAddr
00 0034ee80 75ce7b39 KERNEL32!ReadConsoleInternal+0x15
01 0034ef08 75c6f1a2 KERNEL32!ReadConsoleA+0x40
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib478b54e1cc995a45aafd8e6482de96\mscorlib.ni.dll
02 0034ef50 7190c747 KERNEL32!ReadFileImplementation+0x75
03 0034efc0 720425a3 mscorlib_ni+0x46c747
04 0034efec 720424b2 mscorlib_ni+0xba25a3
05 0034f018 718679c3 mscorlib_ni+0xba24b2
06 0034f030 71867ebf mscorlib_ni+0x3c79c3
07 0034f04c 7217c401 mscorlib_ni+0x3c7ebf
08 0034f05c 71fe7690 mscorlib_ni+0xcdc401
09 0034f064 004a058c mscorlib_ni+0xb47690
WARNING: Frame IP not in any known module. Following frames may be wrong.
0a 0034f0c8 7294eaf6 0x4a058c
0b 0034f0d4 729570c9 clr!CallDescrWorkerInternal+0x34
0c 0034f128 729576f4 clr!CallDescrWorkerWithHandler+0x6b
0d 0034f198 72aeabf1 clr!MethodDescCallSite::CallTargetWorker+0x16a
0e 0034f2c4 72aeace9 clr!RunMain+0x1ad
0f 0034f538 72aeb2eb clr!Assembly::ExecuteMainMethod+0x124
10 0034fa30 72aeb4a1 clr!SystemDomain::ExecuteMainMethod+0x631
11 0034fa88 72aeb3e7 clr!ExecuteEXE+0x4c
12 0034fac8 72a6f7dc clr!_CorExeMainInternal+0xdc
13 0034fb04 7305d6eb clr!_CorExeMain+0x4d
14 0034fb40 730d7f16 mscoreei!_CorExeMain+0x10e
15 0034fb50 730d4de3 MSCOREE!ShellShim__CorExeMain+0x99
16 0034fb58 75c4343d MSCOREE!_CorExeMain_Exported+0x8
17 0034fb64 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
18 0034fba4 77ac9805 ntdll!__RtlUserThreadStart+0x70
19 0034fbbc 00000000 ntdll!_RtlUserThreadStart+0x1b
所以线程 0 似乎使用了 CLR,因此很可能是 .NET 线程。 "RunMain" 看来这是主线了。
1 Id: 223c.2494 Suspend: 1 Teb: fffda000 Unfrozen
# ChildEBP RetAddr
00 00aaf7e4 7627171a ntdll!ZwWaitForMultipleObjects+0x15
01 00aaf880 75c419fc KERNELBASE!WaitForMultipleObjectsEx+0x100
02 00aaf8c8 72a6c4eb KERNEL32!WaitForMultipleObjectsExImplementation+0xe0
03 00aaf934 72a6c440 clr!DebuggerRCThread::MainLoop+0x99
04 00aaf964 72a6c36d clr!DebuggerRCThread::ThreadProc+0xd0
05 00aaf990 75c4343d clr!DebuggerRCThread::ThreadProcStatic+0xc4
06 00aaf99c 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
07 00aaf9dc 77ac9805 ntdll!__RtlUserThreadStart+0x70
08 00aaf9f4 00000000 ntdll!_RtlUserThreadStart+0x1b
所以线程 1 也使用 CLR,也是 .NET 线程。
2 Id: 223c.13b4 Suspend: 1 Teb: fffd7000 Unfrozen
# ChildEBP RetAddr
00 0446f578 7627171a ntdll!ZwWaitForMultipleObjects+0x15
01 0446f614 75c419fc KERNELBASE!WaitForMultipleObjectsEx+0x100
02 0446f65c 72ad6765 KERNEL32!WaitForMultipleObjectsExImplementation+0xe0
03 0446f68c 72a2d5ce clr!FinalizerThread::WaitForFinalizerEvent+0x8a
04 0446f6bc 72a01e29 clr!FinalizerThread::FinalizerThreadWorker+0x5f
05 0446f6d0 72a01e93 clr!ManagedThreadBase_DispatchInner+0x71
06 0446f774 72a01f60 clr!ManagedThreadBase_DispatchMiddle+0x7e
07 0446f7d0 72aea805 clr!ManagedThreadBase_DispatchOuter+0x5b
08 0446f7f8 72aea8cf clr!ManagedThreadBase::FinalizerBase+0x33
09 0446f834 72a15dd1 clr!FinalizerThread::FinalizerThreadStart+0xd4
0a 0446f8d8 75c4343d clr!Thread::intermediateThreadProc+0x55
0b 0446f8e4 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
0c 0446f924 77ac9805 ntdll!__RtlUserThreadStart+0x70
0d 0446f93c 00000000 ntdll!_RtlUserThreadStart+0x1b
线程 2 也使用了 CLR,FinalizerThread
表明这与垃圾收集有关。
3 Id: 223c.1edc Suspend: 1 Teb: fffaf000 Unfrozen
# ChildEBP RetAddr
00 045bf7fc 77adf69f ntdll!ZwWaitForMultipleObjects+0x15
01 045bf990 75c4343d ntdll!TppWaiterpThread+0x32e
02 045bf99c 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
03 045bf9dc 77ac9805 ntdll!__RtlUserThreadStart+0x70
04 045bf9f4 00000000 ntdll!_RtlUserThreadStart+0x1b
线程 3 是本机线程,正在等待某事。
# 4 Id: 223c.230c Suspend: 1 Teb: fffac000 Unfrozen
# ChildEBP RetAddr
00 04a7fbe0 77b2f306 ntdll!DbgBreakPoint
01 04a7fc10 75c4343d ntdll!DbgUiRemoteBreakin+0x3c
02 04a7fc1c 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
03 04a7fc5c 77ac9805 ntdll!__RtlUserThreadStart+0x70
04 04a7fc74 00000000 ntdll!_RtlUserThreadStart+0x1b
线程 4 是由调试器创建的线程,在您的程序输出中不可见,因为它当时不存在。
如果您只想专注于 .NET,则需要一个 .NET 插件。在这里您会发现有一个线程 (运行 Main()
) 和 Finalizer 线程。
0:004> .loadby sos clr
0:004> !threads
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 650 003e60a8 2a020 Preemptive 0224DC38:00000000 003ad308 1 MTA
2 2 13b4 003f2970 2b220 Preemptive 00000000:00000000 003ad308 0 MTA (Finalizer)
因此在给出的示例中,没有 Threadpool 线程。
有线程池
对于线程池线程,输出将变为
0:005> !threads
ThreadCount: 4
UnstartedThread: 0
BackgroundThread: 3
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 2384 004460a8 2a020 Preemptive 022F00F0:00000000 0040d308 1 MTA
2 2 268c 00452970 2b220 Preemptive 00000000:00000000 0040d308 0 MTA (Finalizer)
4 3 2520 0046cad0 1029220 Preemptive 022A8224:00000000 0040d308 0 MTA (Threadpool Worker)
6 4 26d4 00473a18 1029220 Preemptive 022A61E4:00000000 0040d308 0 MTA (Threadpool Worker)
在本机视图中,调用堆栈是
0:004> k
# ChildEBP RetAddr
00 00e2fa04 762715ce ntdll!NtWaitForSingleObject+0x15
01 00e2fa70 75c41194 KERNELBASE!WaitForSingleObjectEx+0x98
02 00e2fa88 72a02396 KERNEL32!WaitForSingleObjectExImplementation+0x75
03 00e2faec 72a025e7 clr!CLRSemaphore::Wait+0xc0
04 00e2fb28 72a02681 clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x132
05 00e2fb94 72a15dd1 clr!ThreadpoolMgr::WorkerThreadStart+0x389
06 00e2fcb0 75c4343d clr!Thread::intermediateThreadProc+0x55
07 00e2fcbc 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
08 00e2fcfc 77ac9805 ntdll!__RtlUserThreadStart+0x70
09 00e2fd14 00000000 ntdll!_RtlUserThreadStart+0x1b
同样,很明显这是一个 Threadpool 线程。
如果线程当前是运行一些.NET代码,你也可以让.NET调用堆栈可见:
0:000> !clrstack
OS Thread Id: 0x2384 (0)
Child SP IP Call Site
0034f2b0 75ce7ed0 [InlinedCallFrame: 0034f2b0]
0034f2ac 7190c747 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0034f2b0 720425a3 [InlinedCallFrame: 0034f2b0] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0034f314 720425a3 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
0034f348 720424b2 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
0034f368 718679c3 System.IO.StreamReader.ReadBuffer() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
0034f378 71867ebf System.IO.StreamReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
0034f394 7217c401 System.IO.TextReader+SyncTextReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
0034f3a4 71fe7690 System.Console.ReadLine() [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
0034f3ac 003a0669 ConsoleApp2.Program.Main(System.String[]) [C:\Users\For example John\Documents\Visual Studio 2017\Projects\ConsoleApp2\Program.cs @ 16]
0034f588 7294eaf6 [GCFrame: 0034f588]
我想了解 TheadPool 的工作原理。像这样的简单控制台应用程序:
public static void Main()
{
foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
{
Console.WriteLine($"thread.ThreadState: {thread.ThreadState}");
}
Console.ReadLine();
}
输出为:
thread.ThreadState: Running
thread.ThreadState: Wait
thread.ThreadState: Wait
thread.ThreadState: Wait
thread.ThreadState: Wait
所以有5个线程,1个运行,4个等待。我的问题是:
这些线程来自哪里?
我猜这 4 个等待线程是 ThreadPool 线程。它们必须在进入我的主要方法之前创建。你能指出创建这些线程的 .Net 源代码吗?我知道我们可以 post 使用 ThreadPool.QueueUserWorkItem 将任务发送到线程池,但是线程池如何获取 task/delegate?我想有一些类似 WinForm UI 线程的消息泵之类的东西,是否有一个后台线程不断检查是否有任何新任务?也可以看看源码吗?
编辑: 感谢 Thomas,所以我关于 ThreadPool 的 assumptions/imaginations 是完全错误的。我会用更合适的例子打开另一个问题。
基础知识
总而言之,您的所有问题都非常深入,但您目前似乎不具备理解所有这些的知识。
首先,您的代码显示的是所有线程的列表,而不仅仅是 .NET 线程。知道这一点很重要。
Where do these threads come from?
一般可以来自
- .NET
- 加载到 .NET 应用程序中的本机代码
- 挂钩
- 调试器
- ...可能更多。
您可以使用调试器并查看调用堆栈以了解这些线程的作用(稍后将介绍)。
Can you point me to the .Net source code that creating those threads?
当 运行 .EXE 文件时,操作系统会创建一个线程。您不会在 .NET 框架中找到它。
应该可以在 .NET 源代码中找到 Finalizer 线程和 Threadpool 线程的位置。
我认为一段相关代码在 Threadpool.cs 的第 1776 行到 1780 行:
[System.Security.SecurityCritical] // auto-generated
[ResourceExposure(ResourceScope.None)]
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
[SuppressUnmanagedCodeSecurity]
internal static extern bool RequestWorkerThread();
基本上 extern
说它不是在 C# 中实现的,而是在一些本机代码中实现的。
how does the ThreadPool pick up a task/delegate?
所有任务都进入队列。再看Threadpool.cs(写作时第71行):
internal sealed class ThreadPoolWorkQueue
它有一个Add()
方法和一个Remove()
方法。
is there one background thread constantly checking if there is any new task?
没有。您的代码将项目插入队列,线程池工作线程从队列中取出项目。
can I see the source code?
与上述相同的位置。粘贴在这里太多了。
正在调试
请注意,您需要一个还可以显示本机线程的调试器。另请注意,调试器会创建一个额外的线程以进入应用程序。
可以使用WinDbg的~
命令查看线程列表:
0:004> ~
0 Id: 223c.650 Suspend: 1 Teb: fffdd000 Unfrozen
1 Id: 223c.2494 Suspend: 1 Teb: fffda000 Unfrozen
2 Id: 223c.13b4 Suspend: 1 Teb: fffd7000 Unfrozen
3 Id: 223c.1edc Suspend: 1 Teb: fffaf000 Unfrozen
. 4 Id: 223c.230c Suspend: 1 Teb: fffac000 Unfrozen
你可以看到所有线程的调用堆栈使用~*k
:
0:004> ~*k
0 Id: 223c.650 Suspend: 1 Teb: fffdd000 Unfrozen
# ChildEBP RetAddr
00 0034ee80 75ce7b39 KERNEL32!ReadConsoleInternal+0x15
01 0034ef08 75c6f1a2 KERNEL32!ReadConsoleA+0x40
*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib478b54e1cc995a45aafd8e6482de96\mscorlib.ni.dll
02 0034ef50 7190c747 KERNEL32!ReadFileImplementation+0x75
03 0034efc0 720425a3 mscorlib_ni+0x46c747
04 0034efec 720424b2 mscorlib_ni+0xba25a3
05 0034f018 718679c3 mscorlib_ni+0xba24b2
06 0034f030 71867ebf mscorlib_ni+0x3c79c3
07 0034f04c 7217c401 mscorlib_ni+0x3c7ebf
08 0034f05c 71fe7690 mscorlib_ni+0xcdc401
09 0034f064 004a058c mscorlib_ni+0xb47690
WARNING: Frame IP not in any known module. Following frames may be wrong.
0a 0034f0c8 7294eaf6 0x4a058c
0b 0034f0d4 729570c9 clr!CallDescrWorkerInternal+0x34
0c 0034f128 729576f4 clr!CallDescrWorkerWithHandler+0x6b
0d 0034f198 72aeabf1 clr!MethodDescCallSite::CallTargetWorker+0x16a
0e 0034f2c4 72aeace9 clr!RunMain+0x1ad
0f 0034f538 72aeb2eb clr!Assembly::ExecuteMainMethod+0x124
10 0034fa30 72aeb4a1 clr!SystemDomain::ExecuteMainMethod+0x631
11 0034fa88 72aeb3e7 clr!ExecuteEXE+0x4c
12 0034fac8 72a6f7dc clr!_CorExeMainInternal+0xdc
13 0034fb04 7305d6eb clr!_CorExeMain+0x4d
14 0034fb40 730d7f16 mscoreei!_CorExeMain+0x10e
15 0034fb50 730d4de3 MSCOREE!ShellShim__CorExeMain+0x99
16 0034fb58 75c4343d MSCOREE!_CorExeMain_Exported+0x8
17 0034fb64 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
18 0034fba4 77ac9805 ntdll!__RtlUserThreadStart+0x70
19 0034fbbc 00000000 ntdll!_RtlUserThreadStart+0x1b
所以线程 0 似乎使用了 CLR,因此很可能是 .NET 线程。 "RunMain" 看来这是主线了。
1 Id: 223c.2494 Suspend: 1 Teb: fffda000 Unfrozen
# ChildEBP RetAddr
00 00aaf7e4 7627171a ntdll!ZwWaitForMultipleObjects+0x15
01 00aaf880 75c419fc KERNELBASE!WaitForMultipleObjectsEx+0x100
02 00aaf8c8 72a6c4eb KERNEL32!WaitForMultipleObjectsExImplementation+0xe0
03 00aaf934 72a6c440 clr!DebuggerRCThread::MainLoop+0x99
04 00aaf964 72a6c36d clr!DebuggerRCThread::ThreadProc+0xd0
05 00aaf990 75c4343d clr!DebuggerRCThread::ThreadProcStatic+0xc4
06 00aaf99c 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
07 00aaf9dc 77ac9805 ntdll!__RtlUserThreadStart+0x70
08 00aaf9f4 00000000 ntdll!_RtlUserThreadStart+0x1b
所以线程 1 也使用 CLR,也是 .NET 线程。
2 Id: 223c.13b4 Suspend: 1 Teb: fffd7000 Unfrozen
# ChildEBP RetAddr
00 0446f578 7627171a ntdll!ZwWaitForMultipleObjects+0x15
01 0446f614 75c419fc KERNELBASE!WaitForMultipleObjectsEx+0x100
02 0446f65c 72ad6765 KERNEL32!WaitForMultipleObjectsExImplementation+0xe0
03 0446f68c 72a2d5ce clr!FinalizerThread::WaitForFinalizerEvent+0x8a
04 0446f6bc 72a01e29 clr!FinalizerThread::FinalizerThreadWorker+0x5f
05 0446f6d0 72a01e93 clr!ManagedThreadBase_DispatchInner+0x71
06 0446f774 72a01f60 clr!ManagedThreadBase_DispatchMiddle+0x7e
07 0446f7d0 72aea805 clr!ManagedThreadBase_DispatchOuter+0x5b
08 0446f7f8 72aea8cf clr!ManagedThreadBase::FinalizerBase+0x33
09 0446f834 72a15dd1 clr!FinalizerThread::FinalizerThreadStart+0xd4
0a 0446f8d8 75c4343d clr!Thread::intermediateThreadProc+0x55
0b 0446f8e4 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
0c 0446f924 77ac9805 ntdll!__RtlUserThreadStart+0x70
0d 0446f93c 00000000 ntdll!_RtlUserThreadStart+0x1b
线程 2 也使用了 CLR,FinalizerThread
表明这与垃圾收集有关。
3 Id: 223c.1edc Suspend: 1 Teb: fffaf000 Unfrozen
# ChildEBP RetAddr
00 045bf7fc 77adf69f ntdll!ZwWaitForMultipleObjects+0x15
01 045bf990 75c4343d ntdll!TppWaiterpThread+0x32e
02 045bf99c 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
03 045bf9dc 77ac9805 ntdll!__RtlUserThreadStart+0x70
04 045bf9f4 00000000 ntdll!_RtlUserThreadStart+0x1b
线程 3 是本机线程,正在等待某事。
# 4 Id: 223c.230c Suspend: 1 Teb: fffac000 Unfrozen
# ChildEBP RetAddr
00 04a7fbe0 77b2f306 ntdll!DbgBreakPoint
01 04a7fc10 75c4343d ntdll!DbgUiRemoteBreakin+0x3c
02 04a7fc1c 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
03 04a7fc5c 77ac9805 ntdll!__RtlUserThreadStart+0x70
04 04a7fc74 00000000 ntdll!_RtlUserThreadStart+0x1b
线程 4 是由调试器创建的线程,在您的程序输出中不可见,因为它当时不存在。
如果您只想专注于 .NET,则需要一个 .NET 插件。在这里您会发现有一个线程 (运行 Main()
) 和 Finalizer 线程。
0:004> .loadby sos clr
0:004> !threads
ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 650 003e60a8 2a020 Preemptive 0224DC38:00000000 003ad308 1 MTA
2 2 13b4 003f2970 2b220 Preemptive 00000000:00000000 003ad308 0 MTA (Finalizer)
因此在给出的示例中,没有 Threadpool 线程。
有线程池
对于线程池线程,输出将变为
0:005> !threads
ThreadCount: 4
UnstartedThread: 0
BackgroundThread: 3
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
Lock
ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception
0 1 2384 004460a8 2a020 Preemptive 022F00F0:00000000 0040d308 1 MTA
2 2 268c 00452970 2b220 Preemptive 00000000:00000000 0040d308 0 MTA (Finalizer)
4 3 2520 0046cad0 1029220 Preemptive 022A8224:00000000 0040d308 0 MTA (Threadpool Worker)
6 4 26d4 00473a18 1029220 Preemptive 022A61E4:00000000 0040d308 0 MTA (Threadpool Worker)
在本机视图中,调用堆栈是
0:004> k
# ChildEBP RetAddr
00 00e2fa04 762715ce ntdll!NtWaitForSingleObject+0x15
01 00e2fa70 75c41194 KERNELBASE!WaitForSingleObjectEx+0x98
02 00e2fa88 72a02396 KERNEL32!WaitForSingleObjectExImplementation+0x75
03 00e2faec 72a025e7 clr!CLRSemaphore::Wait+0xc0
04 00e2fb28 72a02681 clr!ThreadpoolMgr::UnfairSemaphore::Wait+0x132
05 00e2fb94 72a15dd1 clr!ThreadpoolMgr::WorkerThreadStart+0x389
06 00e2fcb0 75c4343d clr!Thread::intermediateThreadProc+0x55
07 00e2fcbc 77ac9832 KERNEL32!BaseThreadInitThunk+0xe
08 00e2fcfc 77ac9805 ntdll!__RtlUserThreadStart+0x70
09 00e2fd14 00000000 ntdll!_RtlUserThreadStart+0x1b
同样,很明显这是一个 Threadpool 线程。
如果线程当前是运行一些.NET代码,你也可以让.NET调用堆栈可见:
0:000> !clrstack
OS Thread Id: 0x2384 (0)
Child SP IP Call Site
0034f2b0 75ce7ed0 [InlinedCallFrame: 0034f2b0]
0034f2ac 7190c747 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0034f2b0 720425a3 [InlinedCallFrame: 0034f2b0] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
0034f314 720425a3 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
0034f348 720424b2 System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
0034f368 718679c3 System.IO.StreamReader.ReadBuffer() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
0034f378 71867ebf System.IO.StreamReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
0034f394 7217c401 System.IO.TextReader+SyncTextReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
0034f3a4 71fe7690 System.Console.ReadLine() [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
0034f3ac 003a0669 ConsoleApp2.Program.Main(System.String[]) [C:\Users\For example John\Documents\Visual Studio 2017\Projects\ConsoleApp2\Program.cs @ 16]
0034f588 7294eaf6 [GCFrame: 0034f588]