许多人可能不太重视线程的终止。其实线程终止需要完成得事情很多,合理运用能发挥很大的作用。首先讲一下终止的方法。
1、”寿终正寝“(正常终止,相应的线程函数返回,如我在CreateThread写的那样,这是最好的方法,相应资源会被释放)
2、“自杀”(调用ExitThread,【【VOID ExitThread(DWORD dwExitCode)】】,不被推荐,因为这样虽然可以讲操作系统分配的资源回收,却不会将自己声明的c/c++资源回收(比如类对象依旧存在)
3、“他杀”(TerminateThread,【【BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode)】】,不推荐)
4、”殉情“(支持线程的进程结束,不推荐)
不管采用哪种方法,当线程终止的时候,
1、该线程拥有的所有用户级别对象句柄都被释放,退出码从STILL_ACTIVE变为了传给相应参数的的dxExitCode的值。
2、很重要的一点是,当终止时,线程的核对象(kernel object,可以理解为操作系统用来记录该线程信息的对象)会变成signaled(在后面的信号量等地方会用到,请牢记这一点!!)
3、如果该线程是相应进程中的最后一个,操作系统也会认为进程结束了
4、线程的kernel object的使用量(usage count)会减少1
下面是代码:
#include <Windows.h>
#include <iostream>
using namespace std;
DWORD WINAPI FirstThread(PVOID pvParam){
*(int*)pvParam=5;
ExitThread(1);
return(0);
}
int main(){
int x;
DWORD dwThreadId;
DWORD exitCode;
HANDLE hThread=CreateThread(NULL,0,FirstThread,&x,0,&dwThreadId);
GetExitCodeThread(hThread,&exitCode);
cout<<"thread exitcode1:"<<exitCode<<endl;
HANDLE hCurrentProcess=GetCurrentProcess();
cout<<"Created Thread ID"<<dwThreadId<<endl<<"Thread Handle:"<<hThread<<endl<<"Current Process Handle is :"<<hCurrentProcess<<endl;
Sleep(200);
GetExitCodeThread(hThread,&exitCode);
cout<<"thread exitcode2:"<<exitCode<<endl;
hThread=CreateThread(NULL,0,FirstThread,&x,0,&dwThreadId);
TerminateThread(hThread,3);
Sleep(200);
GetExitCodeThread(hThread,&exitCode);
cout<<"thread exitcode3:"<<exitCode<<endl;
CloseHandle(hThread);
system("pause");
return 0;
}
最后的输出如下:
259正是STILL_ACTIVE的宏定义(0x00000103L)
这里可能会对sleep的使用有疑问,那么您可以去掉两个sleep试一下,看看结果有什么区别(最好是自己写一下,这样才会有更好地理解)。
我的理解是线程调用create之后不会立马执行到我们自定义的线程函数,而是还有一些操作系统的初始化工作。这时候主线程(即main函数还会继续执行下去,而这时候的线程状态已经标记为STILL_ACTIVE了,所以才会出现上面的情况。为了验证猜想我有编写了下面的程序:
#include <Windows.h>
#include <iostream>
using namespace std;
LARGE_INTEGER Counter1,Counter2,Counter3;
double GetTimeEps(LARGE_INTEGER& count1,LARGE_INTEGER& count2){
LARGE_INTEGER CPUFrequency;
QueryPerformanceFrequency(&CPUFrequency);
double d=(double)(count1.QuadPart-count2.QuadPart)/(double)CPUFrequency.QuadPart*1000.0;
return d;
}
DWORD WINAPI FirstThread(PVOID pvParam){
QueryPerformanceCounter(&Counter3);
cout<<"time interval to mythreadfunction is:"<<GetTimeEps(Counter3,Counter1)<<endl;
*(int*)pvParam=5;
ExitThread(1);
return(0);
}
int main(){
int x;
BOOL bResult=0;
DWORD dwThreadId;
DWORD exitCode;
QueryPerformanceCounter(&Counter1);
HANDLE hThread=CreateThread(NULL,0,FirstThread,&x,0,&dwThreadId);
GetExitCodeThread(hThread,&exitCode);
cout<<"thread exitcode1:"<<exitCode<<endl;
QueryPerformanceCounter(&Counter2);
cout<<"time interval to cout is:"<<GetTimeEps(Counter2,Counter1)<<endl;
HANDLE hCurrentProcess=GetCurrentProcess();
cout<<"Created Thread ID"<<dwThreadId<<endl<<"Thread Handle:"<<hThread<<endl<<"Current Process Handle is :"<<hCurrentProcess<<endl;
Sleep(200);
GetExitCodeThread(hThread,&exitCode);
cout<<"thread exitcode2:"<<exitCode<<endl;
hThread=CreateThread(NULL,0,FirstThread,&x,0,&dwThreadId);
TerminateThread(hThread,3);
Sleep(200);
GetExitCodeThread(hThread,&exitCode);
cout<<"thread exitcode3:"<<exitCode<<endl;
CloseHandle(hThread);
system("pause");
return 0;
}
最后程序的结果如下:
可以很清楚的看到在真正进入我们自定义的线程函数之前有“很长”(相对于线程来说)的时间来进行操作系统方面的初始化工作。