句柄实际上是指向内核对象内存块的指针、访问掩码、标志.(其实微软为了隐藏里面的结构体,故意以void*来表示)
对于Windows的线(进)程而言,系统总是维护一个与当前进程(线)相关的一张句柄表.当我们创建一个进程(线)时,句柄使用计数就会加1,因为继承关系(继承父进程的句柄)而产生后的句柄,使用计数同样加1.但是CloseHandle的实质是告诉进程(线程),当前句柄使用计数减1,如果句柄使用计数减为0,那么内核对象会被销毁,同样线程也会结束.(前提是线程还没有运行),也就是说,当系统要运行的时候,它就会检查句柄使用计数是否为0,如果是0,则退出进(线)程,否则执行进(线)程.
说了一大堆,还是看代码吧!
代码一:
DWORD WINAPI ThreadProc1( PVOID pvParam ) { cout<<"thread one running..."<<endl; for( int i = 0;i< 10000;++ i ) { cout<<"i = :"<<i<<endl; } return 0; } void main() { DWORD dwThread1ID; HANDLE hThread = CreateThread( NULL,0,ThreadProc1,NULL, 0/*CREATE_SUPPEND*/,&dwThread1ID ); CloseHandle( hThread ); getchar(); return; }
使用CloseHandle时,线程已经结束(可以通过Process Explorer来查看)但是输出依然在继续.
这个地方不是很理解.开始以为是10000不够大,cpu实际上已经处理完成,后来加大数字,而且把cout<<"thread one running..."<<endl;移至循环内,经过发现线程还是闪一下就退出了,但是输出并没有结束.这就奇怪了.
代码二:
DWORD WINAPI ThreadProc1( PVOID pvParam ) { cout<<"thread one running..."<<endl; for( int i = 0;i< 10000;++ i ) { cout<<"i = :"<<i<<endl; } return 0; } void main() { DWORD dwThread1ID; HANDLE hThread; hThread = CreateThread( NULL,0,ThreadProc1,NULL,0,&dwThread1ID ); Sleep(100); SuspendThread( hThread ); CloseHandle( hThread ); ResumeThread( hThread );//hThread已经无效,但当线程暂停,当前线程无法操作之前的线程. getchar(); return; }
这个程序开始确实想错了,本来是想测试线程暂停和执行时,内核使用计数对其的影响,后来发现证明我的想法是错的.原因见注释.
这里关于CloseHandle,再回过头看msdn,里面就线程问题专门进行了说明:
Closing a thread handle does not terminate the associated thread. To remove a thread object, you must terminate the thread, then close all handles to the thread.
意思大概是要想终止线程,必须关闭所有的句柄并且终止线程.单独关闭句柄并不能使线程结束.
纠结了很久,再次查看核心编程,它上面是这样说的,调用CloseHandle,如果句柄有效,系统将根据内核对象的地址找到使用计数,然后递减,如果计数为0,内核对象将被销毁,并从内存中除去.那它的意思是创建一个线程的时候,使用计数不是1而是2?如果是1的话,那肯定内核对象将被销毁了.也就不会出现上述代码出现的问题.
那么可以有两种解释:一种是内核对象和线程是两个并不相关的东西.二中解释就是之前所说的计数为2.
前一种解释是不通的:首先内核对象是系统为管理线程而创建的.如果内核对象都销毁了,线程就变成了游离态,这岂不是一个天大的bug.我估计微软也不可能做这么愚蠢的事情.
后一种解释的话,好像不是怎么好理解.实际上核心编程针对CreateProcess有了对内核对象的引用计数的说明.书中指出创建进程内核对象和线程(主)对象的时候,系统会每个对象指定一个初始化引用计数1.然后在CreateProcess返回之前,它使用完全的权限来打开进程进程对象和线程对象.并保存在PROCESS_INFORMATION结构中.这是因为是再一次打开(可以理解成引用),对应的使用技术自然是+1(也就是2).
很悲剧的一件事情,弄了半天居然就这么简单.当初在看的时候也没有太留意.导致出现这么大的错误.后来在网上发现原来已经有人发表过类似的博客了.这更加让人悲剧.