(VC/MFC)多线程(Multi-Threading) -1. 基本概念.

在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函数

你可能感兴趣的:(reading)