C++对象是怎么死的?Win32线程篇


  在前面的帖子 里聊完了进程终止对C++对象析构的影响。今天咱们来说一下线程对于C++对象析构的影响。
  由于C++ 03标准没有包含线程的概念,而C++ 0x尚未正式发布。所以对线程的讨论只好根据特定的操作系统平台来谈。对于操作系统自带的线程API,目前比较流行的款式是Windows平台提供的线程API和POSIX平台上的pthread API。但是这两种线程API的差异实在是太大,没法拿出来一起聊。我只好把“线程篇”的帖子再拆分一下,今天先来聊一聊Win32的线程API。
  另外,对于进行跨平台开发的同学,应该已经用上了某些跨平台的第三方线程库(比如ACE、Boost等),对于这些库的介绍,初步打算放到“C++的可移植性和跨平台开发 ”系列中。

  ★两套API :OS API vs CRT API
  本来照例要先介绍线程的几种死法,但是考虑到很多Windows程序员经常混淆线程API,搞不清楚到底该用哪个。所以先来说一下两套线程API的问题。
  首先,Windows操作系统本身提供了线程的创建函数CreateThread 和销毁函数ExitThread 。其中的CreateThread 用于创建线程,ExitThread 用于在线程函数内部推出线程(也就是自杀)。
  其次,在Visual C++自带的C运行库(以下简称CRT)中,还带了另外4个API函数,分别是:_beginthread ,_endthread ,_beginthreadex ,_endthreadex 。其中的_beginthread 和_beginthreadex 用于创建线程(它们内部调用了CreateThread ),_endthread 和_endthreadex 用于自杀(它们内部调用了ExitThread )。
  有同学看到这里,被搞懵了,心想:“干嘛要搞这么多玩意儿出来糊弄人?有CreateThread 和ExitThread 不就够了嘛!”其实你有所不知,此中大有奥妙啊。
  因为OS API作为操作系统本身提供的API函数,它被设计为语言无关的。它们不光可以被C++调用,还可以被其它诸如VB、Python、Delphi等开发语言来调用。所以它们不会(也不能够)帮你处理一些和具体编程语言相关的琐事。
  而CRT API虽然最终还是要调用OS API来完成核心的功能,但是CRT API在不知不觉中多帮我们干了一些虽琐碎但重要的工作。(如果同学们想窥探一下CRT API内部都干了些啥,可以拜读一下Win32编程的经典名著《Windows 核心编程》的6.7 章节,里面介绍得挺细致的)
  费了这么多口水,无非是要同学们牢记:以后在Windows平台下开发多线程程序,千万不要 直接使用这两个线程API(也就是CreateThread 和ExitThread ),否则后果自负 :-)
  另外,顺便补充一下。除了上述提到的CRT库。其它一些Windows平台的C++库也可能提供了线程的启动函数(比如MFC的AfxBeginThread),这些函数也对OS API进行了包装,所以用起来也是安全的。

  ★三种死法
  说完了两套API,开始来讨论一下线程的几种死法。线程和进程一样,也有三种死法。详见如下:
  1、自然死亡
  一般来说,每个线程都会对应某个函数(以下称为“线程函数”)。线程函数是线程运行的主体。所谓的“自然死亡”,就是通过return 语句结束线程函数的执行。
  2、自杀
  所谓的“自杀”,就是当前线程通过调用某API把自己 给停掉。前面已经说了OS API的坏话,同学们应该明白不能 再用它们。那我们能否使用CRT API来进行自杀呢?请看MSDN上的相关文档 。上面说了,如果使用_endthread 和_endthreadex ,将导致析构函数不被 调用。
  3、它杀
  所谓的“它杀”,很明显,就是其它线程通过调用某API把当前线程给强行 停掉。对于Windows平台来说,实现“它杀”比较简单,使用TernimateThread 就直接干掉了(它杀也是最野蛮的)。

  ★类对象的析构
  照前一个帖子 的风格,还是把类对象分为三种:局部非静态对象、局部静态对象、非局部对象。由于非局部对象是在main之前就创建、在进程死亡时析构,暂时与线程扯不上太大关系。剩下的两种局部对象,在宿主线程(所谓宿主线程,就是创建该局部对象的线程)死亡时会受到什么影响捏?请看如下的对照表:
-------------------------
       局部非静态对象  局部静态对象
    自然死亡   能       能
  自杀     不能       能
  它杀     不能       能
-------------------------
  从上述结果可以看出,Windows上线程的死法还是以自然死亡为最安全,这点和进程的死法类似。所以同学们在Windows上开发时,要尽量避免自杀和它杀。

  ★关于主线程之死
  所谓“主线程”,就是进程启动时,操作系统为该进程默认创建的第一个线程。通俗地讲,可以把main 函数看成是主线程的线程函数。
  主线程之死是有讲究的。由于前面已经阐述了非自然死亡的坏处,所以我们只讨论主线程自然死亡这一种情况。当主线程自然死亡时(也就是用return 从main 返回时),会导致exit 函数被调用,exit 函数就会开始清除当前进程的各种资源,为进程的死亡作准备。这时候,如果还有其它活着的线程,也会被一起干掉(其效果类似于它杀)。
  为了防止出现上述情况,主线程一定要负责最终的善后工作。务必等到其它线程都死了,它才能死。

  Windows平台上,有关线程的对象析构问题,就聊到这。下一个帖子 ,咱们来聊一下pthread相关的对象析构话题。


本文来自CSDN博客,关于对象与线程的话题,收藏了。http://blog.csdn.net/program_think/archive/2009/03/05/3961062.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 创建一个基于对话框的应用程序。并增加如图所示控件;分别为3个进度条控件关联三个进度条类型的变量;并在对话框的初始化函数中,设定进度条的范围;为编辑框关联一个整型的变量;为12个按钮添加消息处理函数; 2. 定义结构体:用做线程函数的参数传递 typedef struct Threadinfo{ CProgressCtrl *progress;//进度条对象 int speed; //进度条速度 int pos; //进度条位置 } thread,*lpthread; 3. 为对话框增加三个句柄,用于标识各个线程; HANDLE hThread1; //线程1线程句柄 HANDLE hThread2; //线程2线程句柄 HANDLE hThread3; //线程3线程句柄 在增加三个结构体类型的变量,用做线程函数的参数传递; HANDLE hThread1; //线程1线程句柄 HANDLE hThread2; //线程2线程句柄 HANDLE hThread3; //线程3线程句柄 4. 新增一个静态的全局变量,用于记录所有线程的状态:static int GlobalVar=10000; 5. 声明并编写线程函数,注意只能有一个参数,且函数的返回值类型也是固定的;函数名可以自定义; DWORD WINAPI ThreadFun(LPVOID pthread);//线程入口函数 6. 在启动按钮的消息处理函数中编写如下代码: thread1.progress=&m_progress1;//进度条对象 thread1.speed=100;//速度 thread1.pos=0;//初始位置 hThread1=CreateThread(NULL,0,ThreadFun,&thread1;,0,0);//创建并开始线程 if (!hThread1) { MessageBox("创建线程失败"); } 7. 编写线程函数(一般是一个循环,或者需要花费时间很长的算法!否者就失去了多线程的意义) DWORD WINAPI ThreadFun(LPVOID pthread) //线程入口函数 { lpthread temp=(lpthread)pthread;//参数强制转换为结构体类型 temp->progress->SetPos(temp->pos); //设置被传递过来的进度条的位置 while(temp->posspeed); /设置速度 temp->pos++; //增加进度 temp->progress->SetPos(temp->pos); //设置进度条的新位置 GlobalVar--; if(temp->pos==20) { temp->pos=0; //进度条满则归0 } } return true; } 8. 在挂起按钮函数中,编写如下代码: if(SuspendThread(hThread1)==0xFFFFFFFF) { MessageBox("挂起失败!进程可能已经死亡或未创建!"); return; } 9. 在执行按钮函数中,编写如下代码: if(ResumeThread(hThread1)==0xFFFFFFFF) { MessageBox("执行失败!进程可能已经死亡或未创建!"); return; } 10. 在停止按钮函数中,编写如下代码: if(TerminateThread(hThread1,0))//前些终止线程 { CloseHandle(hThread1);//销毁线程句柄 } else { MessageBox("终止进程失败!"); } 11. 为应用程序添加WM_TIMER消息,实时更新全局变量的值到编辑框;
Win32线程中可以使用互斥量(Mutex)来控制执行顺序。互斥量是一个同步对象,用于限制对共享资源的访问。 当一个线程需要访问共享资源时,它必须先获得互斥量的所有权,然后才能访问共享资源。如果资源已经被其他线程占用,则该线程将被阻塞,直到资源可用为止。 以下是使用互斥量实现线程同步的示例: ```c++ #include <Windows.h> HANDLE hMutex; DWORD WINAPI ThreadFunc1(LPVOID lpParam) { // 等待互斥量 WaitForSingleObject(hMutex, INFINITE); // 访问共享资源 printf("Thread 1 is accessing the shared resource.\n"); // 释放互斥量 ReleaseMutex(hMutex); return 0; } DWORD WINAPI ThreadFunc2(LPVOID lpParam) { // 等待互斥量 WaitForSingleObject(hMutex, INFINITE); // 访问共享资源 printf("Thread 2 is accessing the shared resource.\n"); // 释放互斥量 ReleaseMutex(hMutex); return 0; } int main() { // 创建互斥量 hMutex = CreateMutex(NULL, FALSE, NULL); // 创建线程 HANDLE hThread1 = CreateThread(NULL, 0, ThreadFunc1, NULL, 0, NULL); HANDLE hThread2 = CreateThread(NULL, 0, ThreadFunc2, NULL, 0, NULL); // 等待线程完成 WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); // 关闭句柄 CloseHandle(hMutex); CloseHandle(hThread1); CloseHandle(hThread2); return 0; } ``` 在上面的示例中,两个线程都需要访问共享资源,但是只有一个线程可以访问该资源。通过使用互斥量,我们可以确保线程之间的执行顺序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值