线程的handle用处:
线程的handle是指向“线程的内核对象”的,而不是指向线程本身.每一个内核对象仅仅是内核分配的一个内存块,而且仅仅能由内核訪问。该内存块是一种数据结构,它的成员负责维护对象的各种信息(eg: 安全性描写叙述,引用计数等)。
CloseHandle()
在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
可是这个handle并不能全然代表这个线程,它不过线程的一个“标识”,系统和用户能够利用它对对应的线程进行必要的操纵。假设在线程成功创建后,不再须要用到这个句柄,就能够在创建成功后,线程退出前直接CloseHandle掉,但这并不会影响到线程的执行。
不运行CloseHandle() 带来的后果:
若在线程运行完之后,没有通过CloseHandle()将引用计数减1,在进程运行期间,将会造成内核对象的泄露,相当与句柄泄露,但不同于内存泄露, 这势必会对系统的效率带来一定程度上的负面影响。可是,请记住,当进程结束退出后,系统仍然会自己主动帮你清理这些资源。可是在这里不推荐这样的做法,毕竟不是 一个良好的编程习惯!
( 应用程序执行时,有可能泄露内核对象,可是当进程终止执行时,系统能确保全部内容均被正确地清除。另外,这个情况是用于全部对象,资源和内存块,也就是说,当进程终止时,系统将保证不会留下不论什么对象。)
TerminateThread()
函数的声明例如以下:
听过无数次不要TerminateThread,仅仅是工作中经常使用,貌似也没有什么问题。今天在高强度測试中发现了一个不可原谅的错误。參看以下的样例
DWORD __stdcall mythread(void*) { while( true ) { char* p = new char[1024]; delete [] p; } } int _tmain(int argc, _TCHAR* argv[]) { HANDLE h = CreateThread(NULL, 0, mythread, NULL, 0, NULL); Sleep(1000); TerminateThread(h , 0); h = NULL; char* p = new char[1024]; // 这里会死锁, 过不去 delete [] p; return 0; }
为什么死锁呢?new操作符用的是小块堆,整个进程在分配和回收内存时,都要用同一把锁。假设一个线程在占用该锁时被杀死(即临死前该线程在new或delete操作中),其它线程就无法再使用new或delete了,表现为hang住。
<核心编程>里明白提醒不要TerminateThread,但原因并非血淋淋滴。今天发现的这个bug印证了此书的价值。
另注:很多暂时的网络操作经经常使用TerminateThread,作为网络不通时的退出机制,以后要改改了。比方让该线程自生自灭,自行退出。
ExitThread是推荐使用的结束一个线程的方法,当调用该函数时,当前线程的栈被释放,然后线程终止,相对于TerminateThread函数来说,这样做可以更好地完毕附加在该线程上的DLL的清除工作
终止线程两个函数:ExitThread() 和 TerminateThread()
若要终止线程的执行,能够使用以下四种的方法:
线程函数退出循环来返回 (最佳方法 )。
通过调用ExitThread 函数,线程将自行撤消(尽量不要使用这样的方法 )。
同一个进程或还有一个进程中的线程调用TerminateThread 函数(最好避免使用这样的方法 )。
该线程的主进程终止执行(避免使用 )。
以下将介绍终止线程执行的方法,而且说明线程终止执行时会出现什么情况。
1.线程函数返回
始终都应该将线程设计成这种形式,即当想要线程终止执行时,它们就行返回。这是
确保全部线程资源被正确地清除的唯一办法。
假设线程可以返回,就行确保下列事项的实现:
a) 在线程函数中创建的全部C + +对象均将通过它们的撤消函数正确地撤消。
b)操作系统将正确地释放线程堆栈使用的内存。
c)系统将线程的退出代码(在线程的内核对象中维护)设置为线程函数的返回值。
d)系统将递减线程内核对象的使用计数。
2.ExitThread 函数
能够让线程调用ExitThread 函数,以便强制线程终止执行:
该函数将终止线程的执行,并导致操作系统清除该线程使用的全部操作系统资源。可是,C++资源(如C++类对象)将不被撤消。因为这个原因,最好从线程函数返回,而不是通过调用ExitThread 来返回。
当然,能够使用ExitThread 的dwExitThread 參数告诉系统将线程的退出代码设置为什么。ExitThread 函数并不返回不论什么值,由于线程已经终止执行,不能执行很多其它的代码。
注意终止线程执行的最佳方法是让它的线程函数返回。可是,假设使用本节介绍的方法,应该知道ExitThread 函数是Windows用来撤消线程的函数。假设编写C/C++代码,那么决不应该调用ExitThread 。应该使用Visual C++执行期库函数_endthreadex。假设不使用Microsoft的Visual C++编译器,你的编译器供应商有它自己的ExitThread 的替代函数。无论这个替代函数是什么,都必须使用。本章后面将说明_endthreadex的作用和它的重要性。
3.TerminateThread 函数
调用TerminateThread 函数也可以终止线程的执行:
与ExitThread 不同,ExitThread 总是撤消调用的线程,而TerminateThread 可以撤消不论什么线程。hThread參数用于标识被终止执行的线程的句柄。当线程终止执行时,它的退出代码成为你作为dwExitThread 參数传递的值。同一时候,线程的内核对象的使用计数也被递减。
注意TerminateThread 函数是异步执行的函数,也就是说,它告诉系统你想要线程终止执行,可是,当函数返回时,不能保证线程被撤消。假设须要确切地知道该线程已经终止执行,必须调用WaitForSingleObject或者类似的函数,传递线程的句柄。
设计良好的应用程序从来不使用这个函数,由于被终止执行的线程收不到它被撤消的通知。线程不能正确地清除,而且不能防止自己被撤消。注意当使用返回或调用ExitThread 的方法撤消线程时,该线程的内存堆栈也被撤消。可是,假设使用TerminateThread ,那么在拥有线程的进程终止执行之前,系统不撤消该线程的堆栈。Microsoft有益用这样的方法来实现TerminateThread 。假设其它仍然正在执行的线程要引用强制撤消的线程堆栈上的值,那么其它的线程就会出现訪问违规的问题。假设将已经撤消的线程的堆栈留在内存中,那么其它线程就能够继续非常好地执行。此外,当线程终止执行时,
DLL通常接收通知。假设使用Terminate Thread 强迫线程终止,DLL就不接收通知,这能阻止适当的清除(具体信息參见第20章)。
4.在进程终止执行时撤消线程
ExitProcess和TerminateProcess函数也能够用来终止线程的执行。区别在于这些线程将会使终止执行的进程中的全部线程全部终止执行。另外,因为整个进程已经被关闭,进程使用的全部资源肯定已被清除。这当然包含全部线程的堆栈。这两个函数会导致进程中的剩余线程被强制撤消,就像从每一个剩余的线程调用TerminateThread 一样。显然,这意味着正确的应用程序清除没有发生,即C++对象撤消函数没有被调用,数据没有转至磁盘等等。