关于Windows Thread

昨天在看老工程中的代码的时候,发现其中创建了一个线程后,并没有通过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;
  }
 }
}

 

### 使用 `std::thread` 进行多线程编程 在 Windows 环境下使用 C++ 的 `std::thread` 库可以方便地创建和管理多个线程。下面是一个简单的例子来展示如何实现一个多线程程序。 #### 创建启动新线程 通过实例化 `std::thread` 对象将目标函数传递给它作为参数,即可轻松创建新的工作线程: ```cpp #include <iostream> #include <thread> void task(int id) { std::cout << "Thread ID: " << id << ", this_thread::get_id(): " << std::this_thread::get_id() << '\n'; } int main() { // Create two threads that execute 'task' std::thread t1(task, 1); std::thread t2(task, 2); // Wait for both threads to finish their execution before continuing. if (t1.joinable()) t1.join(); if (t2.joinable()) t2.join(); return 0; } ``` 此代码片段展示了两个独立的工作线程被创建出来执行相同的任务函数[^1]。 #### 处理返回值与共享数据 当需要处理来自不同线程的结果或共享某些资源时,则可能需要用到互斥锁 (`mutex`) 来保护这些临界区的数据访问安全;也可以考虑采用更高级别的同步机制如条件变量(`condition_variable`) 或者原子操作(`atomic<T>`). 对于想要获取子线程计算结果的情况,可以通过引用传参的方式让子线程修改外部定义好的容器内的元素,或者利用 `future/promise` 组件来进行异步通信。 ```cpp #include <iostream> #include <vector> #include <string> #include <thread> #include <chrono> // A simple function which takes longer time and returns result via reference parameter void longComputation(std::vector<int>& vec){ auto start = std::chrono::steady_clock::now(); while(true){ auto end = std::chrono::steady_clock::now(); if (std::chrono::duration_cast<std::chrono::seconds>(end-start).count()>3){break;} } vec.push_back(42); // Add computed value into vector after computation completes } int main(){ std::vector<int> results; // Launching multiple threads with different tasks... std::thread thd(longComputation,std::ref(results)); // Do other things here... // Join the thread back when done working elsewhere thd.join(); // Print out final outcome from all computations performed by launched threads for(auto& item :results){ std::cout<<item<<"\n"; } return 0; } ``` 这段示范说明了怎样在一个单独的进程中发运行几个不同的活动,且收集它们产生的输出.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值