在Win32环境中,每个运行的应用程序都建立一个进程(Process),每个进程有一个或多个执行线程(Thread)组成.
MFC把执行的线程封装在CWinThread类中,它还包括了同步类,这些类封装了事件,互斥,和可在Windows核心中找到的其他线程同步对象。
MFC区分了两种不同类型的线程: 用户界面线程(user-interface thread) 和工作者线程(worker thread).两者的主要区别在于user-interface thread有消息循环,而工作者线程没有。
user-interface thread 可以创建窗口和处理发送给这些窗口的消息。worker thread执行后台任务,这些后台任务不直接接受用户的输入,因此不需要窗口和消息循环。user-interface thread 最常用的是创建多窗口,这些窗口有分开的执行线程来负责管理。
worker thread 适合于执行孤立的任务,这些任务能够与应用程序的其他部分相脱离,并且当其他处理在前台发生时它在后台执行。 创建worker thread -法1)构造一个CWinThread对象,并调用该对象的CreateThread()函数来创建线程。 -法2)用AfxBeginThread()构造一个CWinThread对象并同时创建一个线程。 ; 注意,不要使用Win32的::CreteThread()函数在MFG程序中创建线程。Win32的这个API 和上述的 CWinThread::CreateThread(),AfxBeginThread()有所不同。 上述的2种方法除了启动线程外,还初始化被框架使用的内部变量,在线程创建过程期间的各个点上执行安全性检查,并采取步骤保用一个线程安全的访问C运行库中的函数。//启动一个worker thread, 并传给它一个应用程序定义的数据结构ThreadInfo的地址。
CwinThread *pThread=AfxBeginThread(ThreadFunc,&ThreadInfo);//ThreadFunc 是线程函数,当线程开始执行时,该函数得以执行。
UNIT ThreadFunc(LPVOID pParam){ UNIT nlterations=(UNIT)pParam; for(UNIT i=0;i<nlterations;i++) return 0; }AfxBeginThread()的原型如下:
CWinThread* AfxBeginThread( AFX_THREADPROC pfnThreadProc, //线程的入口函数,声明一定要如下: UINT MyThreadFunction( LPVOID pParam ),不能设置为NULL; LPVOID pParam, //传递入线程的参数,注意它的类型为:LPVOID,所以我们可以传递一个结构体入线程. int nPriority=THREAD_PRIORITY_NORMAL, //该线程的执行优先级。 UNIT nStackSize=0, //该线程的最大堆栈尺寸。 DWORD dwCreateFlags=0, //默认值0,告诉系统立即开始执行该线程。 LPSCURITY_ATTRIBUTES lpSecurityAttrs=NULL );创建user-interface 线程.- 与创建worker thread 的过程不同,一个worker thread是由它的线程函数定义,但是一个user-interface thread是由一个动态可创建的类来控制,
该类是从CWinThread派生的。 - 一个CUIThread是通过调用AfxBeginThread()来启动的,该函数接受指向该线程类的一个CRuntimeClass指针。 CWinThread *pThread=AfxBeginThread(RUNTIME_CLASS(CUIThread)); AfxBeginThread() 创建线程详解在MFC中,一般用全局函数AfxBeginThread()来创建并初始化一个线程的运行,该函数有两种重载形式,分别用于创建工作者线程和用户界面线程。两种重载函数原型和参数分别说明如下:
(1) CWinThread* AfxBeginThread(AFX_THREADPROC pfnThreadProc,
LPVOID pParam, nPriority=THREAD_PRIORITY_NORMAL, UINT nStackSize=0, DWORD dwCreateFlags=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);PfnThreadProc:指向工作者线程的执行函数的指针,线程函数原型必须声明如下: UINT ExecutingFunction(LPVOID pParam);
请注意,ExecutingFunction()应返回一个UINT类型的值,用以指明该函数结束的原因。一般情况下,返回0表明执行成功。
pParam:传递给线程函数的一个32位参数,执行函数将用某种方式解释该值。它可以是数值,或是指向一个结构的指针,甚至可以被忽略;
nPriority:线程的优先级。如果为0,则线程与其父线程具有相同的优先级;
nStackSize:线程为自己分配堆栈的大小,其单位为字节。如果nStackSize被设为0,则线程的堆栈被设置成与父线程堆栈相同大小;
dwCreateFlags:如果为0,则线程在创建后立刻开始执行。如果为CREATE_SUSPEND,则线程在创建后立刻被挂起;
lpSecurityAttrs:线程的安全属性指针,一般为NULL;
(2) CWinThread* AfxBeginThread(CRuntimeClass* pThreadClass, int nPriority=THREAD_PRIORITY_NORMAL, UINT nStackSize=0, DWORD dwCreateFlags=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL); pThreadClass 是指向 CWinThread 的一个导出类的运行时类对象的指针,该导出类定义了被创建的用户界面线程的启动、退出等;其它参数的意义同形式1。使用函数的这个原型生成的线程也有消息机制,在以后的例子中我们将发现同主线程的机制几乎一样。下面我们对CWinThread类的数据成员及常用函数进行简要说明。
m_hThread:当前线程的句柄;
m_nThreadID:当前线程的ID; m_pMainWnd:指向应用程序主窗口的指针 BOOL CWinThread::CreateThread(DWORD dwCreateFlags=0, UINT nStackSize=0, LPSECURITY_ATTRIBUTES lpSecurityAttrs=NULL);该函数中的dwCreateFlags、nStackSize、lpSecurityAttrs参数和API函数CreateThread中的对应参数有相同含义,该函数执行成功,返回非0值,否则返回0。
一般情况下,调用AfxBeginThread()来一次性地创建并启动一个线程,但是也可以通过两步法来创建线程:首先创建CWinThread类的一个对象,然后调用该对象的成员函数CreateThread()来启动该线程。virtual BOOL CWinThread::InitInstance();
重载该函数以控制用户界面线程实例的初始化。初始化成功则返回非0值,否则返回0。用户界面线程经常重载该函数,工作者线程一般不使用InitInstance()。
virtual int CWinThread::ExitInstance();在线程终结前重载该函数进行一些必要的清理工作。该函数返回线程的退出码,0表示执行成功,非0值用来标识各种错误。同InitInstance()成员函数一样,该函数也只适用于用户界面线程。
挂起和继续执行线程 -挂起: CWinThread::SuspendThread() -继续: CWinthread::ResumeThread()使线程睡眠.
-通过调用API函数 ::Sleep() , 例如 ::Sleep(1000)使自己睡眠。终止一个线程:
-对于worker thread , 当一个worker thread的线程函数执行一个返回语句或者调用AfxEndThread()时,这个worker thread 要终止。 -对于user-interface thread, 当一个WM_QUIT消息被发送到它的消息队列中,或该线程中的一个函数调用AfxEndThread()时,该线程就被终止。 ; 一个线程可以用API函数 ::PostQuitMessage()把一个WM_QUIT消息发送到自身程序上. - AfxEndThrea()和 ::PostQuitMessage()返回一个32位的退出吗,在该线程被终止后,可以用GetExitCodeThread()来检索改码。 DWORD dwExitCode; ::GetExitCodeThread(pThread->m_hThread,&dwExitCode); 如果调用一个仍在执行的线程,则::GetExitcodeThread()把dwExitCode置为STILL_ACTIVE(0x103).从一个线程终止另一个线程:
-一般来说,线程只能自我终止,如果想要线程A终止线程B,必须建立一个信号通知机制,运行线程A告诉线程B终止它自己。 //Thread A static BOOL bContinue=TRUE; CWinThread *pThread=AfxBeginThread(ThreadFunc,&bContinue);//Do some other work
//Save the thread handle.
HANDLE hThrend=pThread-<m_hThread;//Tell thread B to terminate
bContinue=FALSE;//::WaitForSingleObject()一直处于等待状态,知道被指定的对象(另一个线程)输入一个信号为止
//当线程终止时,一个线程对象将由无信号变成有信号的。 //第一个参数:想要等待的对象的句柄。第二个参数:愿意等待的时间长度。 ::WaitForSingleObject(hThread,INFINITE);//Thread B
UINT ThreadFunc(LPVOID pParam){ BOOL *pContinue=(BOOL *)pParam; while(*pContinue){ // Do some work } return 0; }
要结束线程的两种方式
1 : 这是,也就是让线程函数执行完成,此时线程正常结束.它会返回一个值,一般0是成功结束,
当然你可以定义自己的认为合适的值来代表线程成功执行.在线程内调用AfxEndThread将会直接结束线程,此时线
程的一切资源都会被回收.
2 : 如果你想让另一个线程B来结束线程A,那么,你就需要在这两个线程中传递信息.
不管是工作者线程还是界面线程,如果你想在线程结束后得到它的结果,那么你可以调用:
::GetExitCodeThread函数