今天看了点关于Windows多线程的东西,摘抄点关于CloseHandle的内容放于此,以便以后参考。
主要是《Windows核心编程》里的两小节:
3.1.1 内核对象的使用计数
内核对象由内核所拥有,而不是由进程所拥有。换句话说,如果你的进程调用了一个创建内核对象的函数,然后你的进程终止运行,那么内核对象不一定被撤消。在大多数情况下,对象将被撤消,但是如果另一个进程正在使用你的进程创建的内核对象,那么该内核知道,在另一个进程停止使用该对象前不要撤消该对象,必须记住的是,内核对象的存在时间可以比创建该对象的进程长。
内核知道有多少进程正在使用某个内核对象,因为每个对象包含一个使用计数。使用计数是所有内核对象类型常用的数据成员之一。当一个对象刚刚创建时,它的使用计数被置为1。然后,当另一个进程访问一个现有的内核对象时,使用计数就递增1。当进程终止运行时,内核就自动确定该进程仍然打开的所有内核对象的使用计数。如果内核对象的使用计数降为0,内核就撤消该对象。这样可以确保在没有进程引用该对象时系统中不保留任何内核对象。
3.2.2 关闭内核对象
无论怎样创建内核对象,都要向系统指明将通过调用C l o s e H a n d l e来结束对该对象的操作:该函数首先检查调用进程的句柄表,以确保传递给它的索引(句柄)用于标识一个进程实际上无权访问的对象。如果该索引是有效的,那么系统就可以获得内核对象的数据结构的地址,并可确定该结构中的使用计数的数据成员。如果使用计数是0,该内核便从内存中撤消该内核对象。如果将一个无效句柄传递给C l o s e H a n d l e,将会出现两种情况之一。如果进程运行正常,C l o s e H a n d l e返回FA L S E,而G e t L a s t E r r o r则返回E R R O R _ I N VA L I D _ H A N D L E。如果进程正在排除错误,系统将通知调试程序,以便能排除它的错误。
在C l o s e H a n d l e返回之前,它会清除进程的句柄表中的项目,该句柄现在对你的进程已经无效,不应该试图使用它。无论内核对象是否已经撤消,都会发生清除操作。当调用C l o s e H a n d l e函数之后,将不再拥有对内核对象的访问权,不过,如果该对象的使用计数没有递减为0,那么该对象尚未被撤消。这没有问题,它只是意味着一个或多个其他进程正在使用该对象。当其他进程停止使用该对时(通过调用C l o s e H a n d l e),该对象将被撤消。
假如忘记调用C l o s e H a n d l e函数,那么会不会出现内存泄漏呢?答案是可能的,但是也不一定。在进程运行时,进程有可能泄漏资源(如内核对象)。但是,当进程终止运行时,操作系统能够确保该进程使用的任何资源或全部资源均被释放,这是有保证的。对于内核对象来说,系统将执行下列操作:当进程终止运行时,系统会自动扫描进程的句柄表。如果该表拥有任何无效项目(即在终止进程运行前没有关闭的对象),系统将关闭这些对象句柄。如果这些对象中的任何对象的使用计数降为0,那么内核便撤消该对象。
因此,应用程序在运行时有可能泄漏内核对象,但是当进程终止运行时,系统将能确保所有内容均被正确地清除。另外,这个情况适用于所有对象、资源和内存块,也就是说,当进程终止运行时,系统将保证进程不会留下任何对象。
以下是网友的评论:
CreateThread启动了一个线程,同时产生一个句柄让你好操纵这个线程,如果你不要用这个句柄了就CloseHandle关掉它。
调用这个CloseHandle并不意味着结束线程,而是表示不关心此句柄的状态了,也就无法控制子进程的线程了。如果需要关心,可以在子进程结束后再CloseHandle,但一定得CloseHandle。
操作系统内核管理内核对象的生命期,应用程序通过CloseHandle操作内核对象的引用计数,当引用计数由1降为0时,内核负责销毁相应的内核对象。进程和线程都有一个内核对象与它们对应,操作系统通过内核对象管理进程和线程。
本人总结:
当你在程序中,不需要再操作创建的线程时,就CloseHandle掉,即便是那个线程目前计数为1,等你调用CloseHandle后该计数降为0,但已经创建的线程并没有被马上撤消,而是等线程函数执行完毕后才撤消,或者是在线程函数执行完毕前整个进程结束,那么该线程也被撤消。至于为什么在计数降为0时,没有马上撤消该线程,不是很清楚。
今天(2007-08-28)又看了下《Windows核心编程》,有了些新的认识:
6.4:当CreateThread被调用时,系统创建一个线程内核对象。该线程内核对象不是线程本身,而是操作系统用来管理线程的较小的数据结构。………………这与进程和进程内核对象之间的关系是相同的。6.6:调用CreateThread可使系统创建一个线程内核对象。该对象的初始使用计数是2(在线程停止运行和从CreateThread返回的句柄关闭之前,线程内核对象不会被撤销)。
根据最后一条就可以解释我昨天的疑问了^_^
——————————————————————————————————————————————
一,在程序中建立线程的概念
对于一个进程而言,在进程建立后,同时系统也会为进程自动分配一个主线程。拿Main函数而言,当Main函数执行完后,此时主线程就退出了,主线程退出也同时意味着进程结束。
二,线程、内核对象、内核对象引用计数
1.创建一个线程有几种方法,这里我们先学习的是利用CreateThread()函数创建线程,此函数的参数及具体用法参见MSDN。如果创建线程成功,函数则返回一个新的线程句柄。(根据《Windows核心编程》,线程创建时,系统设置线程内核对象的引用计数为1,在Create函数返回前,将会打开线程句柄,所以线程的内核对象引用计数+1)
=================================================================================
CreateThread后那个线程的引用计数不是1,而是2。
creating a new process causes the system to create a process kernel object
and a thread kernel object. At creation time, the system gives each object
an initial usage count of 1. Then, just before CreateProcess returns, the
function opens the process object and the thread object and places the
process-relative handles for each in the hProcess and hThread members of
the PROCESS_INFORMATION structure. When CreateProcess opens these objects
internally, the usage count for each becomes 2. ---摘自《Windows核心编程》
=================================================================================
三,CloseHandle()用法
1.CloseHandel(ThreadHandle );
只是关闭了一个线程句柄对象,表示我不再使用该句柄,对该句柄不感兴趣,即不对这个句柄对应的线程做任何干预了。并没有结束线程,线程本身还在继续运行。如果你CreateThread以后需要对这个线程做一些操作,比如改变优先级,被其他线程等待,强制TermateThread等,就要保存这个句柄,使用完了再CloseHandle()。
2.为什么要CreateThread()和CloseHandle()紧挨配套使用
一方面,所有的内核对象(包括线程Handle)都是系统资源,用了要还的,也就是说用完后一定要CloseHandle关闭之,如果不这么做,你系统的句柄资源很快就用光了,另一方面,由于CreateThread()后线程内核对象的引用计数是2,在CloseHandle()引用计数-1之后,内核对象引用计数仍不为0,不会被释放,所以线程仍运行,直到线程函数执行完毕后,引用计数再-1,线程结束。
还可以查看这篇文章 http://hi.baidu.com/heiheijiushiwo/blog/item/02c29924e518e0318644f979.html
启发:
1 windows 核心编程 以后得看
2 系统管理线程是通过线程内部内核对象的引用计数 当此引用计数为0 时线程被关闭
当在开始 createthread()创建完线程后线程的引用计数为2
调用closehadle()将只关闭不对线程做操作同时将线程引用计数减1
当在线程执行完毕时 引用计数在减1 正常情况下为 0 线程结束