windows线程
以下内容引述《windows核心编程》
线程与进程
进程实际上有两个组成部分:一个进程内核对象和一个地址空间;同样的,线程也有两个组成部分:
- 线程的内核对象,操作系统用它管理线程,系统还用内核对象来存放线程统计信息的地方;
- 线程栈,用于维护线程执行时所需的所有函数参数和局部变量
创建线程
对于用MSVC编译器生成的应用程序,这个线程首先会执行C/C++运行库的启动代码,后者调用入口点函数(_tmain或者_tWinMain),直入口点函数返回C/C++运行库的启动代码,后者最终将调用ExitProcess
HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
DWORD cbStackSize,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD dwCreateFlags,
PDWORD pdwThreadId);
调用CreateThread是,系统会创建一个线程内核对象,该内核对象提供给操作系统用于管理线程。同时,系统从进程的地址空间中分配内存给线程栈使用。新线程与负责创建的那个线程在相同的进程上下文中运行。因此,新线程可以访问进程内核对象的所有句柄、进程中的所有内存以及同一个进程中其他所有线程的栈。
推荐使用_beginthreadex函数在C/C++编程中创建线程。
避免创建线程
在几乎所有的应用程序中,所有的用户界面组件(窗口)都应该共享同一个线程。一个窗口的所有子窗口无疑应该由一个线程来创建。有时,也许需要在不同的线程上创建不同的窗口,但这类情形相当少见。
通常,应用程序有一个用户界面线程,此线程负责创建所有窗口,另外还负责GetMessage循环。进程中的其他所有线程都是受计算机能力的制约或者受I/O(输出/输入)限制的工作线程,这些线程永远不会创建窗口。
用户界面线程的优先级永远高于工作线程。
进程与线程
线程内核对象的寿命至少可以达到他们相关联的线程那样长。不过,对象的寿命可能超过线程本身的寿命。
- 默认情况下,主线程的入口函数必须命名为main,wmain,WinMain或者wWinMain。但是线程函数可以随意命名;
- 进程的函数入参,提供了ANSI/Unicode版本供我们选择;而线程只有一个参数;
- 线程函数必须返回一个值,成为该线程的退出码;类似于C/C++运行库的策略:领主线程的退出代码成为进程的退出代码。
- 线程尽可能使用函数参数和局部变量。
线程终止
终止的方式
- 线程函数返回(推荐)
- 线程调用ExitThread函数“杀死”自己
- 同一个线程或另一个进程中的线程调用TerminateThread函数(避免)
- 包含线程的进程终止运行(避免)
其中1.线程函数返回的方式,是正确处理线程的唯一方式,可以确保一下正确的应用程序清理工作都可以执行
- 线程函数中创建的所有C++对象都通过其析构函数被正确销毁
- 操作系统正确释放线程栈使用的内存
- 操作需提供把线程的退出代码(在线程的内核对象中维护)设为线程函数的返回值;
- 系统递减线程的内核对象的使用计数
而2.调用ExitThread函数的方式,将终止线程的运行,并导致操作系统清理该线程使用的而所有操作系统资源。但是,C/C++资源(如C++类对象)不会被销毁。因为ExitThread函数和CreateThread函数一样,都是windows函数,并不是C/C++函数。使用_endthreadex替代ExitThread函数。
3.调用TerminateThread函数可以杀死一个线程,而不仅仅只有主调线程,TerminateThread可以杀死任意线程。该函数是异步的,需要调涌WaitForSingleObject或类似的函数,并向其传递线程的句柄。
ExitThread和TerminateThread的区别:
ExitThread函数终止线程的运行,该线程的堆栈也会被销毁;如果用TerminateThread,那么除非拥有此线程的进程终止运行,否则系统不会销毁这个线程的堆栈。
此外,动态链接库(DLL)通常会在线程终止运行时收到通知,不过,如果线程是用TerminateThread强行“杀死”的,那么DLL不会收到这个通知,结果是不能执行正常的清理工作。
进程终止和线程终止
进程终止,ExitProcess和TeminateProcess可用于终止线程运行,区别是这些函数会使终止运行的进程中的所有的线程全部终止。同时,由于整个进程都会关闭,所有的系统资源肯定会被清理。其中必然包括所有线程的堆栈。但是这样意味着正确的应用程序清理工作不会执行:c++对象的析构函数不会被调用,数据不会会写到磁盘……
线程终止:
- 线程拥有的所有用户对象句柄被释放(windows中,大多数对象都是由包含了“创建这些对象的线程”的进程拥有的,但是一个线程有两个用户对象,窗口和钩子)。线程退出时,系统会自动销毁由线程创建或安装的任何窗口,并卸载由线程创建或安装的任何钩子。其他对象只有在拥有线程的进程终止时才会被销毁。
- 线程的退出代码从STILL_ACTIVE变成传给ExitThread或者TerminateThread
- 线程的内核对象的状态变成触发状态
- 如果线程是进程中的最后一个活动对象,系统认为进程也终止了
- 线程内核对象的引用计数-1
其他线程调用GetExitCodeThread来检查hThread所标识的那个线程是否终止运行。