MFC对多线程进行了一层简单的封装,在Visual C++中每个线程都是从CWinThread类继承而来的。每一个应用程序的执行都有一个主线程,这个主线程也是从CWinThread类继承而来的。可以利用CWinThread对象创建应用程序执行的其它线程。
MFC用CWinThread对象来表示所有线程。利用MFC可以创建两种线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务。
为了深入了解MFC下创建线程的方法,我们先深入学习一下CWinThread类。CWinThread类在MFC类结构中的位置如下图所示:
图1:CWinThread类在mfc类结构中的位置
下面几个函数是多线程编程中经常用到的几个全局函数:
//创建工作线程
CWinThread* AFXAPIAfxBeginThread(
AFX_THREADPROC pfnThreadProc,//线程函数
LPVOID pParam,//传给线程函数的参数
int nPriority =THREAD_PRIORITY_NORMAL,//线程的优先级
UINT nStackSize = 0,//堆栈大小
DWORD dwCreateFlags = 0,//创建起始状态标志
LPSECURITY_ATTRIBUTES lpSecurityAttrs= NULL//线程的安全属性
);
//创建用户界面线程
CWinThread* AFXAPIAfxBeginThread(
CRuntimeClass* pThreadClass,//从CWinThread派生的类的RUNTIME_CLASS
int nPriority =THREAD_PRIORITY_NORMAL,//线程的优先级
UINT nStackSize = 0,// 堆栈大小
DWORD dwCreateFlags = 0,// 创建起始状态标志
LPSECURITY_ATTRIBUTESlpSecurityAttrs =NULL//线程的安全属性
);
//获取线程对象
CWinThread* AFXAPIAfxGetThread();
//获取当前消息
MSG* AFXAPIAfxGetCurrentMessage();
//结束线程执行
void AFXAPIAfxEndThread(UINTnExitCode,BOOLbDelete =TRUE);
//初始化线程
void AFXAPIAfxInitThread();
//终止线程执行
void AFXAPIAfxTermThread(HINSTANCEhInstTerm =NULL);
仔细阅读以上类的说明能学到不少东西:
(1) CWinThead类通过CreateThread()成员函数来创建线程,这个函数的声明和Win32APICreateThread()的参数相似
(2)每个函数在运行后都有一个句柄和ID号。
(3)通过设置属性m_bAutoDelete,可决定线程在运行结束后线程对象是否自动删除,它的访问权限是public型的,可以直接进行设置。一般情况下,线程对象的生命周期和线程的生命周期一致。如果你想改变线程对象的生命周期,可设置该属性为FALSE。
(4)MFC下的多线程仍然支持线程的挂起和启动。
(5)具有PreTranslateMessage()、PumpMessage()等函数,供用户界面线程的消息机制使用。
在MFC中实际上是调用AfxBeginThread()函数来创建线程的。
(1)工作线程的线程函数:从AfxBeginThread()函数的参数可以看出:
AFX_THREADPROC pfnThreadProc,//线程函数
LPVOID pParam, //传给线程函数的参数
其中AFX_THREADPROC为一个宏,其定义如下:
typedef UINT(AFX_CDECL *AFX_THREADPROC)(LPVOID);
从以上语句,可以得到工作线程的线程函数的形式为:
UINT ThreadFunc(LPVOID pParm);
ThreadFunc()函数应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。
pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略。
(2)用户界面线程的线程函数:从AfxBeginThread()函数的参数可以看出:
CRuntimeClass* pThreadClass,//从CWinThread派生的类的RUNTIME_CLASS
即pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等。
由于用户界面线程含有自己的消息循环,可以处理Windows消息,并可创建和管理诸如窗口和控件等用户界面元素。因此,这种线程较工作线程更为复杂。
创建用户界面线程的起点是从MFC的CWinThread类派生一个定制的线程类,而不是调用AfxBeginThead()函数。定制的线程类必须重载InitInstance()函数,该函数用来执行初始化任务,在创建线程时系统将调用InitInstance()函数。最好还要重载ExitInstane()函数,该函数是InitInstance()函数的对应,MFC在删除线程对象之前会调用ExitInstane()函数,以便线程能够在结束后清除自身。
用户界面线程的创建有两种方法,方法一是首先从CWinThread类派生一个类(必须要用宏DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE对该类进行声明和实现),然后调用AfxBeginThead()创建CWinThread派生类的对象进行初始化,启动线程运行。方法二是先通过构造函数创建类CWinThread的一个对象,然后由程序员调用函数::CreateThread来启动线程。通常CWinThread类的对象在该线程的生存期结束时将自动终止,如果程序员希望自己来控制,则需要将m_bAutoDelete设为FALSE。这样在线程终止之后,CWinThread类对象仍然存在,此时需要手动删除CWinThread对象。