分享

线程、线程句柄、线程ID

 紫殿 2011-09-12

什么是句柄:
    句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?
    为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。
    句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象
线程与线程句柄的关系:

    句柄可以认为是系统对资源(如线程)的分配的一个编号。关闭这个编号,对于不同的资源,效果不尽相同。对于线程来说,关闭这个编号并不意味着终止线程,只是之后很难再操纵这个线程。
线程句柄与线程ID的区别:
CreateThread() API 用于创建线程。 API 返回同时线程句柄和线程标识符 (ID)。线程句柄有完全访问权创建线程对象。 运行线程时线程 ID 唯一标识线程在系统级别。
●ID是在Windows系统范围内唯一标示Thread的。   
●Handle是用来操作Thread的,可以有多个,每个HANDLE可以有不同的操作权限,在不同进程OpenThread得到的值不一样。  
●线程的ID是系统全局的,其HANDLE是进程局部的.

●此ID只在线程的生存期内有效。

●HANDLE是os和client之间用来操作进程和线程一个桥梁,os有一张维护HANDLE的表单,里面大概放置了  HANDLE的引用计数和有关的属性,HANDLE是os标识进程和线程的东西,但是用户也可以用这个来标识进程和线程,对其操作;而ID是os用来标识进程和线程的,并且是全局唯一的,  但用户可以通过这个ID获得进程线程的HANDLE,多次得到的HANDLE并不一定是一样的.HANDLE是内核对象,而ID好像不是,并没有专门创建ID的函数.

●ID是CreateThread时操作系统自动生成的。

●线程的句柄和id是不同的。  
  在windows系统中,线程的id是唯一对应的,也就是说,如果两个线程返回相同的id,则他们必然是同一线程,反之一定是不同的线程。而线程的句柄并不是线程的唯一标识,线程的句柄只是用来访问该线程的的一个32位值,尽管相同的句柄一定标识同一线程,但同一线程可能拥有两个打开的句柄,因此,不能用句柄来区分两个线程是否是同一线程。
线程终止运行时发生的操作
  当线程终止运行时,会发生下列操作:
  ? 线程拥有的所有用户对象均被释放。在 Windows 中,大多数对象是由包含创建这些对象的线程的进程拥有的。但是一个线程拥有两个用户对象,即窗口和挂钩。当线程终止运行时,系统会自动撤消任何窗口,并且卸载线程创建的或安装的任何挂钩。其他对象只有在拥有线程的进程终止运行时才被撤消。
  ? 线程的退出代码从 STILL_ACTIVE 改为传递给 ExitThread 或 TerminateThread 的代码。
  ? 线程内核对象的状态变为已通知。
  ? 如果线程是进程中最后一个活动线程,系统也将进程视为已经终止运行。
  ? 线程内核对象的使用计数递减 1。
  当一个线程终止运行时,在与它相关联的线程内核对象的所有未结束的引用关闭之前,该内核对象不会自动被释放。
  一旦线程不再运行,系统中就没有别的线程能够处理该线程的句柄。然而别的线程可以调用 GetExitcodeThread 来检查由 hThread 标识的线程是否已经终止运行。如果它已经终止运行,则确定它的退出代码:
  BOOL GetExitCodeThread(HANDLE hThread, PDOWRD pdwExitCode);
  退出代码的值在 pdwExitCode 指向的 DWORD 中返回。如果调用 GetExitCodeThread 时线程尚未终止运行,该函数就用 STILL_ACTIVE 标识符(定义为 0x103)填入 DWORD。如果该函数运行成功,便返回 TRUE。

    线程退出的时候内核对象就会被激发, WaitForSingleObject()为堵塞函数,等待线程的内核对象被激发。所以终止线程并释放句柄对象的顺序是:TerminateThread()-->WaitForSingleObject()-->CloseHandle().

线程、线程句柄、线程ID的生成和消失

hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, &dwId);

//至此,新线程,线程句柄,线程ID产生

TerminateThread(hThread, 0);
//至此,线程ID,线程句柄都依然存在

WaitForSingleObject(hThread, INFINITE);

//至此,线程本身和线程ID消失

CloseHandle(hThread);
//至此,线程句柄消失


////////////////////////////////////////////////////////////////

在线程创建后马上调用CloseHandle()是个良好的做法,这里不会影响线程的执行,就是因为即使你close了这个handle,它的内部记数也不为零.   但如果你不关,在线程结束后,那个线程对象将滞留于内存中,也就是说你有handle   leak. 

返回这个handle给你,是让你有机会对这个线程实施外部动作,诸如waitforsingleobject之类.
 
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.
创建新的进程后,记数初始化为1,而函数需要返回进程内核对象的句柄,相当于打开一次新创建的类核对象,记数再加1


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多