VC创建删除线程

本来照例要先介绍线程的几种死法,但是考虑到很多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 函数就会开始清除当前进程的各种资源,为进程的死亡作准备。这时候,如果还有其它活着的线程,也会被一起干掉(其效果类似于它杀)。

  为了防止出现上述情况,主线程一定要负责最终的善后工作。务必等到其它线程都死了,它才能死。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/c395565746c/archive/2010/06/12/5666286.aspx

VC中,可以使用三种多线程同步方法,分别是临界区(CriticalSection)、事件(Event)、互斥体(Mutex)。下面是一个使用这三种方法的多线程程序的示例: #include <windows.h> #include <iostream> using namespace std; int g_iCount = 0; CRITICAL_SECTION g_cs; //定义临界区 HANDLE g_hEvent; //定义事件句柄 HANDLE g_hMutex; //定义互斥体句柄 DWORD WINAPI ThreadFunc(LPVOID lpParam) { for (int i = 0; i < 100000; i++) { EnterCriticalSection(&g_cs); //进入临界区 g_iCount++; //临界区内进行操作 LeaveCriticalSection(&g_cs); //退出临界区 SetEvent(g_hEvent); //设置事件 WaitForSingleObject(g_hMutex, INFINITE); //等待互斥体 g_iCount--; //互斥体中进行操作 ReleaseMutex(g_hMutex); //释放互斥体 } return 0; } int main(int argc, char* argv[]) { HANDLE hThread[2]; DWORD dwThreadId[2]; InitializeCriticalSection(&g_cs); //初始化临界区 g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); //创建事件 g_hMutex = CreateMutex(NULL, FALSE, NULL); //创建互斥体 for (int i = 0; i < 2; i++) { hThread[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, &dwThreadId[i]); //创建线程 } for (int i = 0; i < 2; i++) { WaitForSingleObject(hThread[i], INFINITE); //等待线程结束 } cout << "iCount = " << g_iCount << endl; DeleteCriticalSection(&g_cs); //删除临界区 CloseHandle(g_hEvent); //关闭事件句柄 CloseHandle(g_hMutex); //关闭互斥体句柄 return 0; } 上面的程序中,首先定义了一个全局变量g_iCount,用于计数。然后定义了一个临界区g_cs、一个事件句柄g_hEvent、一个互斥体句柄g_hMutex。在主函数中,首先初始化临界区、创建事件句柄、创建互斥体句柄。然后创建两个线程,每个线程都调用ThreadFunc函数。在ThreadFunc函数中,通过EnterCriticalSection和LeaveCriticalSection进入和退出临界区。通过SetEvent设置事件,并通过WaitForSingleObject等待互斥体。通过ReleaseMutex释放互斥体。最后主函数等待两个线程结束,并输出计数结果。最后删除临界区,关闭事件句柄和互斥体句柄。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值