终止线程 
有两种情况可以使线程结束:控制函数结束或者根本就不允许线程完成,而提前终止它。我们可以想象在WORD中进行后台打印,如果打印结束了,那线程就可以结束了。如果用户中止了打印,那后台打印线程也要终止了。本文将主要介绍对这两种情况的实现,并且介绍如何获得线程的结束代码。

1.对于工作线程,结束它是比较容易的:退出线程函数然后返回一个结束原因的代码就是了。用户可以使用AfxEndThread函数或直接利用return返回。通常0代表成功返回,这不是硬性规定,一切要取决于你了。对于用户界面线程,调用::PostQuitMessage,它所要的唯一的参数就是返回代码,也就是工作线程中的那个码,性质是一样的。0通常代表成功。

2.提前终止一个线程也不难:在线程函数中调用AfxEndThread就是了,其中要传入的参数就是返回代码。这会停止线程的执行,释放线程栈,及与线程相关的DLL,并从内存中删除线程对象。AfxEndThread必须在线程函数内调用,如果用户希望从一个线程结束另一个线程,则需要在两个线程间建立通信机制。

如果需要获得线程返回代码,只需要调用::GetExitCodeThread就可以了。这个函数的具体作用就看大家具体去查帮助了。它传入的是线程的句柄,和一个提向返回代码的指针。将来就从那个指针得到返回代码。如果线程仍然处于活动状态,那么::GetExitCodeThread得到的返回代码为STILL_ACTIVE,如果已经退出则得到的是返回代码的地址。获得CWinThread对象的返回代码还需要一点麻烦,通常,当CWinThread线程结束时,线程对象就删除了,因为这个对象不存在了,也就没有办法访问对象的m_hThread变量了,为了避免这种情况,可以有两种方法:

将m_bAutoDelete设置为FALSE,这使得线程结束后CWinThread对象仍然存在,这样用户就可以访问m_hThread了,但是如果用户使用这种方法,用户需要自己析构CWinThread对象。这种方法是推荐的方法。

下一个方法是另外保存线程的句柄。在线程创建后,将m_hThread保存在另一个变量中,以后访问这个变量就是了。但是要小心,在复制句柄以前线程并没有结束,最安全的方法是在AfxBeginThread中传入CREATE_SUSPENDED,保存句柄,然后通过调用ResumeThread,重新开始线程。这两种方法都可以帮助用户得到CWinThread对象的返回代码。

对于Worker线程,终止线程可以使用线程的退出码作为返回值从线程函数返回。

对于UI线程,因为有消息循环,需要发送一个WM_QUIT消息到线程的消息队列,当线程接收到WM_QUIT消息时退出消息循环。因此,结束线程可以在线程内部调用SDK的PostQuitMessage函数,发送WM_QUIT消息。
PostQuitMessage函数的定义如下:

void PostQuitMessage(int nExitCode);

其中:

nExitCode:线程的退出码。

MFC还提供了AfxEndThread函数,Worker线程和UI线程都可以通过在线程内部调用AfxEndThread函数结束线程。

AfxEndThread函数的定义如下:

void AfxEndThread(UINT nExitCode, BOOL bDelete = TRUE);

其中:

nExitCode:线程的退出码。

在MFC的THRDCORE.CPP中,AfxEndThread函数的相关代码如下:

// THRDCORE.CPP
void AFXAPI AfxEndThread(UINT nExitCode, BOOL bDelete)
{
// remove current CWinThread object from memory
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
CWinThread* pThread = pState->m_pCurrentWinThread;
if (pThread != NULL)
{
ASSERT_VALID(pThread);
ASSERT(pThread != AfxGetApp());
// cleanup OLE if required
if (pThread->m_lpfnOleTermOrFreeLib != NULL)
(*pThread->m_lpfnOleTermOrFreeLib)(TRUE, FALSE);
if (bDelete)
pThread->Delete();
pState->m_pCurrentWinThread = NULL;
}
// allow cleanup of any thread local objects
AfxTermThread();
// allow C-runtime to cleanup, and exit the thread
_endthreadex(nExitCode);
}

从MFC代码中可以看出,AfxEndThread函数通过调用_endthreadex函数终止线程。此外,函数还进行释放线程的堆栈、删除线程对象等工作。

如果在其它线程中终止该线程,必须采用线程通信的方法实现。其中一种简单的方法是建立一个变量,让线程监视该变量,当该变量为某个值时,则终止线程。

(1)创建1个基于对话框的应用程序,名称为Demo。

(2)在IDD_DEMO_DIALOG对话框资源中添加控件,如表所示。

类型 ID 标题
Static IDC_STATIC 数据:
Edit IDC_DATA  
Button IDC_BEGIN_THREAD 启动线程
Button IDC_END_THREAD 终止线程

(3)在文件中定义线程传递参数的数据结构,代码如下:

// DemoDlg.h
typedef struct THREAD_PARAM
{
HWND hWnd;
int nData;
BOOL bExit;
}_THREAD_PARAM;

(4)在CDemoDlg类中添加成员变量,代码如下:

// DemoDlg.h
protected:
CWinThread* m_pThread;
THREAD_PARAM m_ThreadParam;

(5)在CDemoDlg类的构造函数中初始化成员变量,代码如下:

// DemoDlg.cpp
CDemoDlg::CDemoDlg(CWnd* pParent /*=NULL*/)
: CDialog(CDemoDlg::IDD, pParent)
{
// ...
m_pThread = NULL;
m_ThreadParam.nData = ;
}

(6)在CDemoDlg类的OnInitDialog函数中添加如下代码:

// DemoDlg.cpp  
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// …
SetDlgItemInt(IDC_DATA, m_nData);
return TRUE;
}

(7)在文件中定义线程消息,代码如下:

// DemoDlg.h
#define WM_THREADMSG WM_USER+1

(8)在文件中定义线程函数,代码如下:

// DemoDlg.h
UINT ThreadProc(LPVOID pParam);
// DemoDlg.cpp
UINT ThreadProc(LPVOID pParam)
{
//线程参数
THREAD_PARAM* pThreadParam = (THREAD_PARAM*)pParam;
while (!pThreadParam->bExit)
{
Sleep();
pThreadParam->nData++;
//向主线程窗口发送消息
::PostMessage(pThreadParam->hWnd, WM_THREADMSG, , );
}
return ;
}

(9)在CDemoDlg类中分别为Button控件添加BN_CLICKED添加消息处理函数,代码如下:

// DemoDlg.cpp
void CDemoDlg::OnBeginThread()
{
if (m_pThread != NULL)
{
AfxMessageBox(_T("线程已经启动。"));
return;
}
m_ThreadParam.hWnd = m_hWnd;
m_ThreadParam.bExit = FALSE; //启动线程,初始为挂起状态
m_pThread = AfxBeginThread(ThreadProc, &m_ThreadParam,
THREAD_PRIORITY_ABOVE_NORMAL, , CREATE_SUSPENDED); //线程结束时不自动删除
m_pThread->m_bAutoDelete = FALSE; //恢复线程运行
m_pThread->ResumeThread();
} void CDemoDlg::OnEndThread()
{
if (m_pThread == NULL)
{
AfxMessageBox(_T("线程已经终止。"));
return;
}
m_ThreadParam.bExit = TRUE; //等待线程结束
::WaitForSingleObject(m_pThread->m_hThread, INFINITE);
delete m_pThread;
m_pThread = NULL;
}

(10)在CDemoDlg类中添加自定义消息处理函数,代码如下:

// DemoDlg.h
afx_msg LRESULT OnMsgFunc(); // DemoDlg.cpp
BEGIN_MESSAGE_MAP(CDemoDlg, CDialog)
ON_MESSAGE(WM_THREADMSG, OnMsgFunc)
END_MESSAGE_MAP() LRESULT CDemoDlg::OnMsgFunc()
{
SetDlgItemInt(IDC_DATA, m_ThreadParam.nData);
return ;  
}

安全终止MFC线程的更多相关文章

  1. java 线程的终止与线程中断

    关于线程终止: 1.一般来讲线程在执行完毕后就会进入死亡状态,那该线程自然就终止了. 2.一些服务端的程序,可能在业务上需要,常驻系统.它本身是一个无穷的循环,用于提供服务.那对于这种线程我们该如何结 ...

  2. MFC 线程创建方式

    MFC 分UI线程和工作线程,一般现在的应用程序都是一个主UI线程和N个工作线程来完成工作.主UI线程获取到工作线程发送的信息来刷新界面. 不过因为工作需要,MFC有要维护的项目,因此就学习一下MFC ...

  3. 【C/C++多线程编程之四】终止pthread线程

    多线程编程之终止pthread线程       Pthread是 POSIX threads 的简称,是POSIX的线程标准.           终止线程似乎是多线程编程的最后一步,但绝不是本系列教 ...

  4. 第5章 不要让线程成为脱缰的野马(Keeping your Threads on Leash) ---干净的终止一个线程

    干净的终止一个线程  我曾经在第2章产生一个后台线程,用以输出一张屏幕外的 bitmap 图.我们必须解决的一个最复杂的问题就是,如果用户企图结束程序,而这张bitmap 图尚未完成,怎么办?第2章的 ...

  5. MFC 线程中CWnd对象

    尽量不要在MFC线程中将CWnd作为参数传递,会引起crash 正确的做法: 1. 将CWnd对应的handle传进来,通过CWnd::FromHandle()函数转换: 2. 在线程中用SendMe ...

  6. java 线程实现、线程暂停和终止 、线程联合join、线程基本信息获取和设置、线程优先级

    转载地址:速学堂 https://www.sxt.cn/Java_jQuery_in_action/eleven-inheritthread.html 1. 通过继承Thread类实现多线程 继承Th ...

  7. MFC 线程启动、暂停、继续、终止

    CWinThread* p_myThread;//创建线程指针 BOOL flag_myThread = FALSE;//是否终止 //头文件中声明(放类内) static UINT MyThread ...

  8. 如何终止java线程

    http://blog.csdn.net/anhuidelinger/article/details/11746365 终止线程的三种方法 有三种方法可以使终止线程. 1.  使用退出标志,使线程正常 ...

  9. MFC线程钩子和全局钩子[HOOK DLL]

    第一部分:API函数简介 1.       SetWindowsHookEx函数 函数原型 HHOOK SetWindowsHookEx( int idHook,        // hook typ ...

随机推荐

  1. 欧洲用户放弃Android转投iOS原因大起底

    据报道,近期在欧洲,有32.4%的新ios设备iPhone用户之前使用的是Android设备.那么为什么欧洲会有这么多Android用户会纷纷抛弃原有操作系统平台而转投iPhone呢? ios设备的同 ...

  2. Unable to locate Android SDK used by project

    Unable to locate Android SDK used by project: DJIgojava.lang.RuntimeException: Unable to locate Andr ...

  3. IREP_SOA Integration WSDL概述(概念)

    20150827 Created By BaoXinjian

  4. VMWare虚拟机下RedHat 9.0linux的网络设置

    VMWare虚拟机下安装的RedHat 9.0 linux有三种方式实现上网,桥接.nat.host-only.本来想用桥接方式的,可总是因为配置网络出现问题而不能上网,还把 sygate4.5(代理 ...

  5. UNIX网络编程

    UNIX网络编程--socket的keep http://www.68idc.cn/help/opersys/unixbsd/20150731471448.html

  6. C++ operator 知识点 2

    http://blog.csdn.net/szlanny/article/details/4295854 operator它有两种用法,一种是operator overloading(操作符重载),一 ...

  7. JAVA 数组实例-求学生成绩的最大成绩,获取数组中的最大值、最小值

    实例: import java.util.*; //求学生最大成绩 public class Test{ public static void main(String[] args){ System. ...

  8. 树莓派,使用opencv调用自带的摄像头

    OpenCV&Pi Cam – Step 1 : Install It’s quite easy to install your new Pi Camera. Installation pro ...

  9. 不容错过错的xcode 插件 转至于cocoachina

    古人云“工欲善其事必先利其器”,打造一个强大的开发环境,是立即提升自身战斗力的绝佳途径!以下是搜集的一些有力的XCode插件.   1.全能搜索家CodePilot 2.0 你要找的是文件?是文件夹? ...

  10. pragma mark - 合成图

    #pragma mark - 合成图 - (UIImage *)getShareImageShell:(UIImage *)shareImage { if (shareImage) { CGSize ...