以下文字片段摘自书籍<<Windows 并发编程指南>>,所有权归原著者,这里仅做交流分享.
线程创建的具体流程
当Windows 创建一个新的线程时,无论是通过Win32的API还是.NET框架的API,都会执行以下的步骤(大致是这个顺序)
- 分配一些重要的线程数据结构,例如 KTHREAD,ETHREAD 以及TEB. 我们在上面已经介绍了这些结构。此外,还将分配和初始化一些结构用于异步过程调用(Asynchronous Procedure Call, APC)、本地过程调用(Local Procedure Call, LPC)、内存管理,I/O, 互斥体所有权,以及线程创建信息等。然后将生成一个唯一的线程ID。
- 分配线程的上下文,其中包好了特定于CPU的寄存器信息。这将生成一个CONTEXT 结构,它将在随后上下文切换期间被用于捕获和恢复处理器的状态。我们可以通过GetUserContext来访问这个数据结构。
- 创建进程地址空间中的用户态栈。我们可以在创建线程或者配置线程信息时通过参数来控制线程栈的保留大小和提交大小,这在前面已经介绍过了。随后,将创建和初始化内核态栈。
- Windows 子系统进程CRSS.exe 将收到新线程创建的通知,此时它可以记录一些在初始化线程状态和执行线程时需要的信息。
- 进程的第一个线程再执行线程起始执行函数之前必须完成进程的初始化工作,包括加载所需的DLL,通过附加到进程的调试器打开调试端口,初始化系统服务,初始化TLS以及相关的数据结构,并且将DLL_PROCESS_ATTACH通知发送给进程中所有已加载的DLL的DllMain函数。
- 将DLL_THREAD_ATTACH 通知发送给进程中的所有DLL。
- 如果在创建线程时没有设置CREATE_SUSPENDED标志,那么线程将立即开始运行。Windows线程调度器将把它分配到某个处理器上执行。在这些操作完成之后,线程将从线程起始执行函数开始执行。
- 线程创建函数返回。在Win32 的CreateThread情况中,返回值是新线程的句柄,而出口参数线程ID将被设置为分配给这个线程的唯一标识。
线程终止的具体流程
我们已经看到了,根据线程是顺利地退出还是通过TerminateThread被强制结束,线程的终止过程将略有不同。正如在线程创建过程中有一些共同的步骤,在线程终止过程中通常也有一些共同的步骤。在前面已经介绍了在线程终止过程中需要注意的各种异常。
- 将DLL_THREAD_DETACH通知发送到进程中每个已加载的DLL。TerminateThread将跳过这个步骤。
- 线程内核对象被设置为已触发状态。触发线程对象意味着,你可以像其他Win32同步事件或者原语那样来使用线程的句柄。
- 释放用户态的栈。与DLL通知的情况一样,TerminateThread将不会执行这个步骤。相反,被强行结束的线程中的用户态栈将在进程退出时被释放。
- 所有的内核态栈数据结构,包括栈、上下文、TEB、TLS内存以及在前面提到的在创建过程中分配的其他与线程相关的数据结构都将被释放。