CloseHandle(),TerminateThread(),ExitThread()的差别
线程的handle用处:
线程的handle是指向“线程的内核对象”的,而不是指向线程本身.每一个内核对象仅仅是内核分配的一个内存块,而且仅仅能由内核訪问。该内存块是一种数据结构,它的成员负责维护对象的各种信息(eg: 安全性描写叙述,引用计数等)。
CloseHandle()
在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
可是这个handle并不能全然代表这个线程,它不过线程的一个“标识”,系统和用户能够利用它对对应的线程进行必要的操纵。假设在线程成功创建后,不再须要用到这个句柄,就能够在创建成功后,线程退出前直接CloseHandle掉,但这并不会影响到线程的执行。
不运行CloseHandle() 带来的后果:
若在线程运行完之后,没有通过CloseHandle()将引用计数减1,在进程运行期间,将会造成内核对象的泄露,相当与句柄泄露,但不同于内存泄露, 这势必会对系统的效率带来一定程度上的负面影响。可是,请记住,当进程结束退出后,系统仍然会自己主动帮你清理这些资源。可是在这里不推荐这样的做法,毕竟不是 一个良好的编程习惯!
( 应用程序执行时,有可能泄露内核对象,可是当进程终止执行时,系统能确保全部内容均被正确地清除。另外,这个情况是用于全部对象,资源和内存块,也就是说,当进程终止时,系统将保证不会留下不论什么对象。)
TerminateThread()
函数的声明例如以下:
BOOL TerminateThread( HANDLE hThread, DWORD dwExitCode);
作用:
在线程外终止一个线程,用于强制终止线程。
參数说明:
HANDLE htread:被终止的线程的句柄,为CWinThread指针。
DWORD dwExitCode:退出码。
返回值:
函数运行成功则返回非零值,运行失败返回0。调用getlasterror获得返回的值。
听过无数次不要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++对象撤消函数没有被调用,数据没有转至磁盘等等。
CloseHandle(),TerminateThread(),ExitThread()的差别的更多相关文章
- CloseHandle(),TerminateThread(),ExitThread()的区别
线程的handle用处:线程的handle是指向“线程的内核对象”的,而不是指向线程本身.每个内核对象只是内核分配的一个内存块,并且只能由内核访问.该内存块是一种数据结构,它的成员负责维护对象的各种信 ...
- C++windows内核编程笔记day13 进程、线程与信号量
Windows进程 进程是一个容器,包括程序运行须要的代码.数据.资源等信息, windows进程的特点: 每一个进程都有自己的ID号 每一个进程都有自己的地址空间.进程之间无法訪问对方的地址空间. ...
- 转:Delphi中使用比较少的一些语法
http://www.cnblogs.com/Murphieston/p/5577836.html 本文是为了加强记忆而写,这里写的大多数内容都是在编程的日常工作中使用频率不高的东西,但是又十分重要. ...
- [16]Windows内核情景分析 --- 服务管理
随时可以看到任务管理器中有一个services.exe进程,这个就是系统的服务控制管理进程,简称SCM 这个进程专门用来管理服务(启动.停止.删除.配置等操作) 系统中所有注册的服务都登记在\HKEY ...
- CloseHandle()函数的使用
CloseHandle()函数的使用?? 很多程序在创建线程都这样写的:............ThreadHandle = CreateThread(NULL,0,.....);CloseHande ...
- CWinThread类,使用后要不要使用CloseHandle释放内核
在VC++中用AfxBeginThread()开启线程的时候,返回的是CWinThead类的指针.但是使用后是否应该用CloseHandle释放内核资源呢? 在<Windows核心编程>中 ...
- 线程创建后为什么要调用CloseHandle
很多程序在创建线程都这样写的: ............ ThreadHandle = CreateThread(NULL,0,.....); CloseHandel(ThreadHandle ); ...
- ROLAP和MOLAP的概念和差别
ROLAP和MOLAP的概念和差别OLAP(on-Line Analysis Processing)是使分析人员.管理人员或执行人员能够从多角度对信息进行快速.一致.交互地存取,从而获得对数据的更深入 ...
- apt-get upgarde和dist-upgrade的差别
apt-get upgarde和dist-upgrade的差别 apt-get upgarde和dist-upgrade的差别 Debian/Ubuntu Linux都使用apt,升级时都是: ...
随机推荐
- 改ucosii的中断禁止和恢复代码,这是一个荒谬的错误【 mrs msr】
ucosii原来的禁止中断以及恢复中断的代码是最简的,但是使用之前,必须声明一个固定名为 OS_CPU_SR cpu_sr 的变量,吊在那里感觉很怪. ;********************* ...
- Swift中的ViewController
ViewController是iOS应用程序中重要的部分,是应用程序数据和视图之间的重要桥梁,ViewController管理应用中的众多视图.iOS的SDK中提供很多原生ViewController ...
- 在Delphi开发的服务中调用指定应用程序
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://fxh7622.blog.51cto.com/63841/529033 在很多时候 ...
- 模拟QQ系统设置面板实现功能
业务需求: 基于网盘客户端的实现,原有网盘的设置面板无论从界面显示还是从业务需求都不能满足我们的正常需求.当前的要求是,模拟QQ系统设置的面板实现当前我们网盘中的基本配置功能.在完成这篇文章时已将基本 ...
- 承诺消费换4G无线上网伴侣活动火热来袭,各指定营业厅即可办理
承诺消费换4G无线上网伴侣活动火热来袭,各指定营业厅即可办理 承诺消费换4G无线上网伴侣活动火热来袭,各指定营业厅即可办理
- CuSparse 第一章
(部分翻译) 第一章 介绍 1. 命名惯例 CUSPARSE 包含了一系列处理稀疏矩阵的基本的线性代数子程式.是cuda函数库的一部分,从C,C++中调用. 该库例程可以分为四类: 第一层:在稠密向量 ...
- 利用opencv中的级联分类器进行人脸检測-opencv学习(1)
OpenCV支持的目标检測的方法是利用样本的Haar特征进行的分类器训练,得到的级联boosted分类器(Cascade Classification).注意,新版本号的C++接口除了Haar特征以外 ...
- o(n)解决问题:调整数组顺序是奇数位于偶数的前面
问题描述: 输入一个整数数组,调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分 void reOrder(int *a,int len) { if(a==NULL || ...
- int 转换成 CString(VC2008里有这个问题)
int s = 123; CString str; str.Format("%d",s); 这样就可以了,但是有的会提示这个错误 如果出现这个错误,就改成下面这个就OK了: st ...
- 基于visual Studio2013解决C语言竞赛题之1049抓牌排序
题目 解决代码及点评 /* 功能:插入排序.许多玩牌的人是以这样的方式来对他们手中的牌进行排序的: 设手中原有3张牌已排好序,抓1张新牌,若这张新牌的次序在原来的第2张牌之后,第 3 ...