1.MFC多线程简介
MFC对多线程进行了一层简单的封装,在Visual C++中每个线程都是从CWinThread类继承而来的。每一个应用程序的执行都有一个主线程,这个主线程也是从CWinThread类继承而来的。可以利用CWinThread对象创建应用程序执行的其它线程。
MFC用CWinThread对象来表示所有线程。利用MFC可以创建两种线程,分别称之为工作者线程和用户界面线程。二者的主要区别在于工作者线程没有消息循环,而用户界面线程有自己的消息队列和消息循环。工作者线程没有消息机制,通常用来执行后台计算和维护任务,如冗长的计算过程,打印机的后台打印等。用户界面线程一般用于处理独立于其他线程执行之外的用户输入,响应用户及系统所产生的事件和消息等。但对于Win32的API编程而言,这两种线程是没有区别的,它们都只需线程的启动地址即可启动线程来执行任务。
2.MFC多线程基础
为了深入了解MFC下创建线程的方法,我们先深入学习一下CWinThread类。CWinThread类在MFC类结构中的位置如下图所示:
在创建一个新的线程时,不必直接创建线程对象,因为线程对象是由全局函数AxCreateThread()自动产生的。只要首先定义一个CWinThread类指针,然后调用全局函数AxCreateThread()来产生一个新的线程对象,并调用线程类的CreateThread()成员函数来具体创建线程,最后将新的线程对象的指针返回。应该将其存在CWinThread变量中,以便能够进一步控制线程。
AxCreateThread()函数和CWinThread:: CreateThread()函数并不是简单地对_beginthreadex()函数进行了一下封装,它还做了一些应用程序框架所需的内部数据的初始化工作,并保证使用正确的C运行库版本,因为在两个函数一开始就要检查环境参数_MT是否已经定义。而且在创建线程过程中还进行了很多检查和测试,保证能够正确产生新的线程,同时在线程创建失败时正确地释放掉已经分配的资源。
注意事项:
(1)一般情况下,推荐使用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。
(2)MFC支持两类多线程,即工作线程和用户界面线程。用户界面线程经常重载InitInstance()和ExitInstance()函数,用以控制用户界面线程实例的初始化和必要的清理工作。工作者线程一般不使用。
3.线程函数
(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 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等。
有了上面的基础,下面来学习线程的创建。
4.工作线程的创建
工作线程没有消息机制,经常用来完成一些后台工作,如计算、打印、等待、循环等,这样用户就不必因为计算机在从事繁杂而耗时的工作而等待。创建一个工作线程相对比较简单。
我们用一个实例来演示工作线程的创建。查找N(很大)以内的素数,为了演示效果,N的取值很大(比如:10000等),并在主界面实时动态显示处理结果,并显示处理进度等。
先说下素数的定义:素数又称质数,指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。
一个数 n 如果是素数,那么它的所有的因子不超过sqrt(n)(即n的平方),那么我们可以用这个性质用判断一个数是否是素数。在此不讨论该算法的复杂度,只是演示效果,望大牛们不要拍砖哈。
5、总之,线程在MFC中经常被用来相互交互传数据和解析使用
最常用的就是使用后台的线程来更新前台窗口上现实的数据,这就是两个线程的作用,使得消息在其中可以互相呼应;
还是要清楚后台线程是没有消息循环的,而窗口线程由于继承了CWnd,所有有自己的消息循环和消息队列,这点是很重要的,当然这样的回调就是为了使得前台显示能够和后台程序同步。