关闭线程句柄CloseHandle()

    技术2022-07-10  151

    自制工具   翰华Box:https://hanhuabox.lanzous.com/b00zjq9uf

    翰华Box - 开发日志:https://blog.csdn.net/qq_41517936/article/details/106409456

    内核对象&句柄&泄漏&检测:https://blog.csdn.net/qq_41517936/article/details/107060344

    关于线程句柄个人总结:(此处为易语言,其他语言需看各自的函数说明)

    启动线程(欲执行的子程序,参数数据,线程句柄),参数<3>的名称为“线程句柄”,类型为“整数型(int)”,可以被省略,提供参数数据时只能提供变量。如果提供了本参数,将向参数变量写入线程句柄(Windows下为HANDLE,Linux下为pthread_t),请在适当的时机关闭该句柄。如果不提供本参数接收线程句柄,内部将自动处理线程句柄。

    1.不填写参数3(线程句柄),那么线程在执行完毕后会自动关闭句柄! 2.填写参数3,那么当线程执行完毕 或 强制结束 时,必须手动关闭句柄,否则会有句柄泄漏的可能!(不手动关闭句柄的话,线程内核对象依然存在(具体看下文),当达到一定数量时,句柄泄漏程序崩溃)

    看需求是否需要线程句柄,如果线程有时候需要强制结束,那么肯定要填了,否则不需要!

    ---------------------------------

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

    1、创建线程

    可以通过调用CreateThread函数来创建一个线程,函数原型如下:

    HANDLE CreateThread(   LPSECURITY_ATTRIBUTES lpsa, //线程安全属性   DWORD cbStack,  //堆栈大小   LPTHREAD_START_ROUTINE lpStartAddr, //线程函数   LPVOID lpvThreadParam, //线程参数   DWORD fdwCreate, //线程创建属性   LPDWORD lpIDThread //线程ID ); 

    此乃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的析构函数将不会被执行。

    原文:https://www.it610.com/article/4023670.htm

    // 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;   }  } }

    内核对象&句柄&泄漏&检测:https://blog.csdn.net/qq_41517936/article/details/107060344

    Processed: 0.012, SQL: 9