windows thread 是否有必要调用closehandle 关闭线程句柄

from:http://blog.csdn.net/njuitjf/article/details/5315047



 

关于Windows Thread

分类: Windows   154人阅读  评论(0)  收藏  举报

昨天在看老工程中的代码的时候,发现其中创建了一个线程后,并没有通过CloseHandle函数来关闭该句柄。抱着怀疑的态度,就查了些相关的资料,现把自己的调查结果总结如下。

1、创建线程
可以通过调用CreateThread函数来创建一个线程,函数原型如下:
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpsa, 
  DWORD cbStack, 
  LPTHREAD_START_ROUTINE lpStartAddr, 
  LPVOID lpvThreadParam, 
  DWORD fdwCreate, 
  LPDWORD lpIDThread
); 
此乃Windows API,MSDN中有详细介绍,在此就不细说了。就说明两点:
一是参数lpStartAddr,这是线程开始的地址,也就是新创建的线程开始执行的地方,一般将一个函数(线程函数)的地址传递给该参数。
二是参数lpvThreadParam,这是传递给一中所说的线程函数的参数。
调用CreateThread时,系统会创建一个线程内核对象,函数CreateThread返回的句柄,可以看作是线程内核对象的一个地址,通过该句柄,你可以操作该线程对象。线程对象的默认引用计数为2,一个是句柄,另一个是线程本身。

2、线程函数
线程函数的类型并无限制,返回值可以是任意类型,自己定义的类型也可以。参数也无限制,你甚至可以定义多个参数。不过,由于创建线程的时候,只传入了一个LPVOID类型的参数,所以,定义的线程函数最好也只包含一个LPVOID类型的参数,如果需要多个参数,可以传递一个结构体指针,然后强制转换。

3、结束线程
结束线程共有四中方法,线程函数返回、ExitThread函数、TerminateThread函数、在进程终止运行时撤消线程。方法间的差别在《Windows核心编程》中有详细介绍,我在此就不重复了。在此,我只说明其中要注意的地方。
最好结束线程的方法就是通过线程函数返回,这样可以释放掉所有的资源,包括系统资源以及C++资源。可以通过函数返回值来设置线程退出码(Exit Code)。
可以在线程函数中调用ExitThread函数来结束自己,其参数为线程退出码(Exit Code)。通过ExitThread函数结束线程时,可以释放系统资源,但是不能释放C++资源,如:若在线程函数中定义了一个类的对象,若通过ExitThread函数结束线程,则不会调用该对象的析构函数,需要在其析构函数中释放的资源将不能被释放。
通过TerminateThread函数,可以强制结束一个线程,其中第一个参数是线程句柄,第二个参数是线程退出码(Exit Code)。通过ExitThread函数结束线程,线程的栈将被销毁,而通过TerminateThread函数结束线程,线程的栈将不被销毁,因为线程是被强制结束的,系统认为可能有其他线程还会使用到该线程的资源。

4、线程内核对象的销毁
当线程内核对象的引用计数为0时,系统将销毁线程内核对象。也就是说,线程内核对象的销毁必须满足两个条件:
一是线程所有的句柄都被close掉(通过函数CloseHandle)。
二是线程必须已经被结束。
若线程正在运行,此时,虽然通过CloseHandle关闭了所有与线程相关的句柄,线程内核对象仍然不会被销毁,只有等线程结束时,线程内核对象才会被销毁。
若未通过CloseHandle关闭线程句柄,虽然线程结束了,线程内核对象仍然不会被销毁。
调用CloseHandle,将会释放Handle占用的资源,同时将线程内核对象的引用计数减一。
结束线程的运行,也会将线程内核对象的引用计数减一。

下面是我的测试代码,在windows xp下,通过vs 2005创建一个win32的控制台程序。
通过测试发现,如果标记1的代码被注释掉,通过任务管理器查看(选择进程选项卡,查看->选择列,选中“句柄计数”),发现句柄数比正常要多一个,也就是说线程句柄没有被释放,自然,线程内核对象也没办法在进程退出前被销毁。
若标记2的代码未被注释掉,则类TestClass的析构函数将不会被执行。

// ThreadTest_Win32.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <windows.h>

char QUIT_EVENT_NAME[] = "Quit Event";

void MainFunction( void );
int SubThreadFunction( void *pContext );


int _tmain(int argc, _TCHAR* argv[])
{
 printf( "Entry Main/n");

 MainFunction();

 printf( "Exit Main/n");

 return 0;
}

void MainFunction()
{
 // Create thread
 HANDLE hSubThread = ::CreateThread(NULL, 0, ( LPTHREAD_START_ROUTINE )SubThreadFunction, NULL, 0, NULL);

 if ( NULL == hSubThread ) {
  return;
 }

 // Create quit event
 HANDLE hQuitEvent = ::CreateEvent( 0, FALSE, FALSE, (LPCWSTR)QUIT_EVENT_NAME );

 if ( NULL == hQuitEvent ){
  ::CloseHandle( hSubThread ); //Should call CloseHandle to close Handle. 标记1
  return;
 }

 for ( INT i = 0; i < 10; ++i ) {
  printf( "Main Thread/n" );
  Sleep( 1000 );
 }

 ::SetEvent( hQuitEvent );
 ::CloseHandle( hQuitEvent );

 ::CloseHandle( hSubThread ); //Should call CloseHandle to close Handle. 标记1

 return;
}

class TestClass
{
public:

 TestClass()
 {
  printf( "TestClass:Constructor/n" );
 }

 ~TestClass()
 {
  printf( "TestClass:Distructor/n" );
 }
};

int SubThreadFunction( PVOID pContext )
{
 TestClass object;

 // Create quit event
 HANDLE hQuitEvent = ::CreateEvent( 0, FALSE, FALSE, (LPCWSTR)QUIT_EVENT_NAME );

 if ( NULL == hQuitEvent ){
  return 1;
 }

 // Wait for quit
 while( TRUE ) {
  printf( "Sub Thread/n" );

  DWORD dwWaitResult = ::WaitForSingleObject( hQuitEvent, 1000 );

  if ( WAIT_OBJECT_0 == dwWaitResult ) {
   ::CloseHandle(hQuitEvent);
//   ::ExitThread( 0 ); // If use ExitThread to end the thread, the distructor of TestClass will not be called. 标记2
   return 100;
  }
 }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值