MFC的进程和线程_第1页
MFC的进程和线程_第2页
MFC的进程和线程_第3页
MFC的进程和线程_第4页
MFC的进程和线程_第5页
已阅读5页,还剩43页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第11章MFC的进程和线程本章讲述多线程编程技术。11.1Win32的进程和线程概念首先有必要了解一下进程和线程的概念。11.1.1进程的概念进程的定义是为执行程序指令的线程而保留的一系列资源的集合。进程是一个可执行的程序,由私有虚拟地址空间、代码、数据和其他操作系统资源(如进程创建的文件、管道、同步对象等)组成。进程是一些所有权的集合,一个进程拥有内存,CPU运行时间等一系列资源,为线程的运行提供一个环境,每个进程都有它自己的地址空间和动态分配的内存,以及线程,文件和其他一些模块。11.1.2线程的概念一个应用程序可以有一个或多个进程,一个进程可以有一个或多个线程,其中一个是主线程。线程是操作系统分时调度分配CPU时间的基本实体。一个线程可以执行程序的任意部分的代码,即使这部分代码被另一个线程并发地执行;一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。11.2进程编程因为MFC没有提供类处理进程,所以直接使用了Win32API函数。11.2.1进程的创建调用CreateProcess函数创建新的进程,运行指定的程序。CreateProcess的原型如下:BOOLCreateProcess(LPCTSTRlpApplicationName,LPTSTRlpCommandLine,LPSECURITY_ATTRIBUTESlpProcessAttributes,LPSECURITY_ATTRIBUTESlpThreadAttributes,BOOLbInheritHandles,DWORDdwCreationFlags,LPVOIDlpEnvironment,LPCTSTRlpCurrentDirectory,LPSTARTUPINFOlpStartupInfo,LPPROCESS_INFORMATIONlpProcessInformation);11.2.2进程的管理和终止取得当前进程的句柄和ID需要以下两个函数:HANDLEGetCurrentProcess(void)DWORDGetCurrentProcessId(void)11.2.3取得和设置进程的优先级取得一个进程的优先级的函数如下:DWORDGetPriorityClass(HANDLEhProcess);其中,参数hProcess是要取得优先级的进程的句柄。设置一个进程的优先级的函数如下: BOOLSetPriorityClass(HANDLEhProcess,DWORDdwPriorityClass);11.2.4进程的终止终止一个进程有两种方法:最常用的方法是调用函数ExitProcess()结束进程。另一种方法是调用函数TerminateProcess终止进程。在当前进程中的一个线程调用函数ExitProcess就会结束当前进程。VOIDExitProcess(UNITuExitCode);当需要在当前进程中结束其他进程时,就需要用到另一种方法即调用函数TerminateProcess,其函数原形为:VOIDTerminateProcess(UNITuExitCode);11.2.5判断一个进程是否终止当一个进程终止或结束时就不能利用这个进程的句柄再对该进程进行操作,这就需要判断一个进程是否终止,或者要看一个进程是否正常的退出,也需要查看这个进程的返回码。可以利用函数GetExitCodeProcess来判断一个进程是否终止,如果终止,则取得这个进程的返回码,否则返回标志STILL_ACTIVE。BOOLGetExitCodeProcess(HANDLEhProcess,LPDWORDlpExitCode);11.3Win32中关于多线程的几个函数下表11.1列出了Win32中关于多线程的几个函数CreateThread创建一个新线程CreatRemoteThread在另一个进程中创建一个新线程ExitThread正常的结束一个线程的执行TerminateThread终止一个线程的执行GetExitCodeThread得到另一个线程的退出码GetThreadPriority得到线程的优先级SetThreadPriority设置一个线程的优先级SuspendThread挂起一个线程ResumeThread重启一个线程CloseHand关闭一个线程的句柄11.3.1线程的创建使用CreateThread函数创建线程,CreateThread的原型如下:HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,//creationflagsLPDWORDlpThreadId);11.3.2CreatRemoteThread函数该函数在其他进程的虚拟地址空间创建线程。其声明如下:HANDLECreatRemoteThread(HANDLEhProcess;LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORDdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOIDlpParameter,DWORDdwCreationFlags,//creationflagsLPDWORDlpThreadId);11.3.3SuspendThread和

ResumeThread函数SuspendThread()的作用为暂停一个线程,ResumeThread()的作用为重启一个线程。它们都带有一个HANDLE型的参数,参数值为要暂停或重启线程的句柄。SuspendThread(HANDLEhThread);ResumeThread(HANDLEhThread);11.3.4ExitThread和TerminateThread函数终止一个线程有两种方法:最常用的方法是调用函数ExitThread()结束线程。另一种方法是调用函数TerminateThread终止线程。在当前线程中的一个线程调用函数ExitProcess就会结束当前线程:VOIDExitThread(DWORDdwExitCode);这个函数用来结束当前线程,其中参数用来存放此线程的退出码。这是最正常的结束线程的方法。BOOLTerminateThread(HANDLEhThread.//线程句柄DWORDdwExitCode//线程退出码);11.3.5取得一个线程的优先级的函数获得一个线程的优先级的函数:intGetThreadPriority(HANDLEhThread);参数hThread是要取得优先级的线程的句柄。设置一个线程的优先级的函数:BOOLSetThreadPriority(HANDLEhThread,intnPriority);参数hThread是要取得优先级线程的句柄,参数nPriority就是要设置的优先级。11.4MFC中多线程的实现在Win32API的基础之上,MFC提供了处理线程的类和函数。MFC对多线程进行一种简单的封装,其中每个线程都是从CWinThread类继承而来的。每一个应用程序的执行都有一个主线程,主线程也是从CWinThread类继承而来的。可以利用CWinThread对象创建应用程序执行的其他线程。处理线程的类是CWinThread,它的成员变量m_hThread和m_hThreadID是对应的Win32线程句柄和线程ID。MFC多线程编程中经常用到的几个全局函数是AfxBeginThread、AfxEndThread等。MFC明确区分两种线程:用户界面线程(Userinterfacethread)和工作者线程(Workerthread)。用户界面线程一般用于处理用户输入并对用户产生的事件和消息作出应答。工作者线程用于完成不要求用户输入的任务,如实时数据采集、计算等。11.4.1与多线程编程相关的全局函数AfxBeginThread用户界面线程和工作者线程都是由AfxBeginThread创建的。:用户界面线程的AfxBeginThread的原型如下:CWinThread*AFXAPIAfxBeginThread(CRuntimeClass*pThreadClass,intnPriority,UINTnStackSize,DWORDdwCreateFlags,LPSECURITY_ATTRIBUTESlpSecurityAttrs)11.4.1与多线程编程相关的全局函数(续)工作者线程的AfxBeginThread的原型如下:CWinThread*AFXAPIAfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,intnPriority,UINTnStackSize,DWORDdwCreateFlags,LPSECURITY_ATTRIBUTESlpSecurityAttrs)11.4.1与多线程编程相关的全局函数(续)CWinThread*AFXAPIAfxGetThread();该全局函数用来获取线程对象。VOIDAFXAPIAfxEndThread(UNITnExitCode,BOOLbDelete=TRUE);该函数用来结束线程的执行。VOIDAFXAPIAfxInitThread();初始化进程函数。VOIDAFXAPIAfxTermThread(HINSTANCEhInstTerm=NULL);终止线程执行函数。11.4.2CWinThread类CWinThread类封装了上节讲的API函数,并且增加了新的函数和属性。至于其类的具体声明在afxwin.h中。11.4.3工作者线程的创建工作线程经常来完成一些后台工作,如计算,打印等,这样用户就不必因为计算机在从事繁杂而耗时的工作而等待。需要向AfxBeginThread()函数提供线程函数的起始地址和传给线程函数的参数。线程函数的格式如下:UNIT函数名(LPVOIDpParam)11.4.4创建用户界面线程用户界面线程的创建有两种方法。方法一是首先从CWinThread类派生一个类(需要用宏DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE对该类进行声明和实现),然后调用函数AfxBeginThread()创建CWinThread派生类的对象进行初始化,启动线程执行。方法二先通过构造函数创建类的一个对象,然后由程序员调用函数CWinThread::CreateThread来启动线程。通常CWinThread类的对象在该线程的生存期结束时将自动终止,如果程序员希望自己来控制,则需要将m_AutoDelete设为FALSE。这样在线程终止之后,CWinThread类对象仍然存在,此时需要手动删除CWinThread对象。11.5线程之间的通信线程通信一般有四种方式:全局变量方式,消息传递方式,参数传递方式和线程同步方式。全局变量方式:在一个进程中共享全局变量就可以通过全局变量来进行线程间的通信参数传递方式:主线程在创建子进程的时候,可以通过传给线程函数的参数和其通信。所传递的参数是一个32位的指针,该指针可以指向简单的数据,也可以指向结构甚至更复杂的数据类型。通过参数的传递能在两个线程函数之间传递很复杂的数据。消息传递法:通过函数在主线程和工作线程之间传递消息,通过函数在用户界面线程和其他线程之间传递消息,消息传递是一种很重要的线程之间的通信方式。线程之间通信的一种重要的方法就是线程同步,将在下一节给予介绍。11.6线程的调度和同步Win32提供了一组对象用来实现多线程的同步。它们是Critical_section(关键段),Event(事件),Mutex(互斥对象),Semaphores(信号量)。MFC封装了这几个同步对象,它们分别是:CCritical_section、Cevent、Cmutex、Csemaphores。这四个同步类都以CsyncObject为它们的父类。11.6.1临界段对象临界段对象一次只允许一个线程取得一个数据区进行操作。这时候可以创建临界段对象,并且使用这个临界段对象进行相应的操作以实现线程的同步。定义一个临界段对象。临界段对象的变量类型是CRITICAL_SECTION:CRITICAL_SECTION对象名;然后调用函数InitializeCriticalSection初始化该对象。初始化时把对象设置为NOT_SINGALED,表示允许线程使用资源:函数说明如下:InitializeCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);11.6.1临界段对象(续)进入和离开临界区。如果一段程序代码需要对某个资源进行同步保护,则这是一段临界段代码。在进入该关键段代码前调用EnterCriticalSection函数,这样,其他线程都不能执行该段代码,若它们试图执行就会被阻塞。完成关键段的执行之后,调用LeaveCriticalSection函数,其他的线程就可以继续执行该段代码。如果该函数不被调用,则其他线程将无限期的等待。VOIDEnterCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);VOIDLeaveCriticalSection(LPCRITICAL_SECTIONlpCriticalSection);两个函数的参数lpCriticalSection是指向CRITICAL_SECTION结构的指针。11.6.2互斥对象互斥对象的工作方式和临界段对象非常相似,其区别在于互斥量不仅保护一个进程内的共享资源,而且保护系统中进程之间的资源,它是通过互斥量提供一个互斥量名来实现进程和线程之间共享协调的。在使用互斥量进行同步线程前,必须首先创建互斥量可以调用CreateMutex函数创建互斥量,其函数说明如下:HANDLECreateMutex(LPSECURITY_ATTRIBUTESlpMutexAttributes,BOOLbInitialOwner;LPCSTRlpName);11.6.2互斥对象(续)打开互斥量的函数 OpenMutex( DWORDdwDesiredAccess,BOOLbInitialOwner;LPCSTRlpName );获得互斥量: 获得互斥量的函数如下: DWORDSignalObjectAndWait( HANDLEhObjectToSignal,HANDLEhObjectToWaitOn, DWORDdwMilliseconds,BOOLbAlertable );释放互斥量:调用函数RealseMutex可以释放互斥量:BOOLRealseMutex(HANDLEhMutex);11.6.3事件对象事件和互斥量的区别如下:事件主要用于协调两个和多个线程之间的动作,使其协调一致,符合逻辑。一个线程等待某个事件的发生,另一个线程则在某个事件发生后产生一个信号,通知正在等待的线程,而互斥量主要是保证任一时刻只有一个线程在使用共享的资源,什么时刻运行哪个线程是随机的,是由操作系统决定的,用户没有任何决定权,所以互斥量不能使两个线程按一定顺序执行。有信号和无信号的含义不同:对于互斥量来讲,有信号状态就是指线程正在拥有互斥量,其他线程不能获得互斥量,无信号是指没有线程拥有互斥量,其他线程可以获得互斥量,访问被互斥量保护的资源。对于事件而讲,当等待的事件发生时,事件对象处于有信号状态;相反当等待的事件没有发生时,称事件处于无信号状态。11.6.3事件对象(续)创建和打开事件对象: 在利用事件之前,必须先调用CreateEvent函数创建一个事件对象 HANDLECreateEvent( LPSECURITY_ATTRIBUTESlpEventAttributes,BOOLbInitialState;BOOLbManulReset;LPCSTRlpName ); HANDLEOpenEvent( DWORDdwDesiredAccess,BOOLbInitialHandle;LPCSTRlpName );设置和重置事件对象: BOOLSetEvent(HANDLEhEvent); 该函数触发一个事件,即将事件置为有信号状态。 BOOLResetEvent(HANDLEhEvent); 该函数将一个事件对象重置为无信号状态。 BOOLPlusEvent(HANDLEhEvent);最后,使用CloseHandle销毁创建的事件对象。11.6.4信号量对象在线程之间进行同步的原因大致有两个:一个是由于线程之间竞争共享的资源,一个是为了完成某种任务而协作。通过互斥可以实现线程之间由于竞争所需要的同步,通过事件可以实现线程之间由于协作而需要的同步。原则上讲,使用互斥量和事件可以解决所有线程之间的同步问题。而信号量很好的将互斥量和事件结合起来,同时解决了线程的竞争和协作的问题。它是对事件同步的推广——在信号量之中有一个内置的计数值,用于对资源进行计数。同时它通过内置的互斥机制保证在有多个线程试图对计数值进行修改时,在任何一个时刻只有一个线程对计数值进行修改。11.6.4信号量对象(续)创建和打开信号量对象: 首先在使用信号量对象,必须调用函数CreateSemaphore创建它。 HANDLECreateSemaphore( LPSECURITY_ATTRIBUTESlpSemaphoreAttributes, BOOLbInitialCount LONGlMaximumCount; LPCSTRlpName }11.6.4信号量对象(续)可以调用函数OpenSemaphore来获得信号量句柄: HANDLEOpenSemaphore{ DWORDwDesiredAccess, BOOLbInitialHandle; LPCSTRlpName };获得和释放信号量, BOOLRealseSemaphore{ HANDLEhSemaphore, LONGlRealseCount, LPLONGlpPreviousCount, };当这个信号量对象不再需要时,就应该调用函数CloseHandle来释放这个信号量对象,从内存中消除。11.6.5各种同步方法的比较 本章讲述了四种线程同步方法,它们分别使互斥量,临界段,事件和信号量,这些方法有各自的特点,用于不同的场合。下面比较一下这些方法的异同:互斥量、事件和信号量都是内核对象,可用于进程之间的同步;临界段是进程内对象只能用于线程之间的同步。虽然在一个进程内实现同步时,临界段对象和互斥量相似,但是在性能上临界段对象要优于互斥量。事件和其他几个同步方法的不同在于事件的主要作用不是保护进程共享资源,而是用于等待某个事件和特定事件发生时发送信号,以协调线程之间的动作。信号量和其他同步方法的区别在于它允许一个以上的线程同时访问共享的资源,而其他同步的方法都保证同时只能有一个线程访问共享的资源。信号量的主要功能在于用于资源计数。同步方法的选择要根据应用场合、同步目的和各种同步对象各自的特点来选择,下面介绍选择同步方法的一些原则,可以根据这些原则来选择同步对象:首先线程在访问共享资源之前是否要等待某个事件的发生。比如,在线程访问一个共享文件前是否要从通信端口接收信息。如果是这样,就可以用事件同步;在一个应用程序中是否有多个线程可以同时访问的共享资源,如果是则选择信号量;是否有多个进程使用共享资源,如果是则选择互斥量;如果以上条件都不满足,则使用临界段就可以了。11.7应用实例排火车的游戏大家都玩过吧?其规则是这样的:将扑克牌分成两份,每人拿一份,二人轮流出牌。如果你出的牌和前面的某张牌一样(不区分花色),则从那张一样的牌到你出的牌都被你拿走,这样轮流出牌,直到其中一人的牌出完为止。最后手上有牌的一方为胜,图11.1。在这个程序中要用到事件同步。因为人和计算机不能同时出牌。比较合理的做法是人出完牌后通知计算机,然后计算机出牌,计算机出完牌之后通知人,计算机处于等待状态。11.7应用实例(续) 首先创建基于对话框工程Eg11_1:1.在VC++集成开发环境中,通过菜单File|New,弹出New对话框;2.在Projects选项卡中选择MFCAppWizard(exe),在Projectname中输入“Eg11_1”Location读者可以自己选择;3.按下OK按钮,在弹出的MFCAppWizardStep-1对话框中选择程序框架为单文档框架,即选中SingleDocument;4.一直接受默认选项,直到MFCAppWizardStep-6,把CEg11_1View类的基类选择为CFormView。如图11.2所示。5.按下“Finish”按钮,在弹出的NewProjectInformation对话框中按下“OK”按钮后等待创建完相应的工程。11.7.1用户界面的设计可以参照图11.1和表11.2来设计界面11.7.2新增成员变量及初始化在CEg11_1View.h中增加一些新的变量:public: CEg11_1Doc*GetDocument();

//事件对象,表示计算机出牌完成,等待游戏者出牌CEventm_computerready; //事件对象,表示游戏者出牌完成,等待计算机出牌 CEventm_playerready; //游戏正在进行的标志 BOOLm_playing; //当前牌局中牌的张数 intm_cardnum; //当前计算机成绩即计算机牌的张数 intm_computernum; //当前游戏者牌的牌的张数 intm_playernum; //当前牌局 intm_card[100];11.7.2新增成员变量及初始化(续)在构造函数中初始化事件对象和其他变量:CEg11_1View::CEg11_1View() :CFormView(CEg11_1View::IDD),m_computerready(TRUE),m_playerready(FALSE){ //{{AFX_DATA_INIT(CEg11_1View) //NOTE:theClassWizardwilladdmemberinitializationhere //}}AFX_DATA_INIT //TODO:addconstructioncodeherem_playing=false;m_cardnum=0;m_computernum=24;m_playernum=24;}11.7.2新增成员变量及初始化(续)通过ClassWizard添加成员函数OnInitialUpdate(),重载这个函数如下:voidCEg11_1View::OnInitialUpdate(){ CFormView::OnInitialUpdate(); GetParentFrame()->RecalcLayout(); ResizeParentToFit();

GetDlgItem(IDC_DISCARD)->EnableWindow(FALSE); SetDlgItemText(IDC_STATIC_SCORE1,"24"); SetDlgItemText(IDC_STATIC_SCORE2,"24");}11.7.3创建菜单响应函数通过ClassWizard为菜单ID_FILE_BEGIN和ID_FILE_END添加菜单响应函数,接受系统默认的函数名,编辑函数代码如下:voidCEg11_1View::OnFileBegin(){ //TODO:Addyourcommandhandlercodehere

m_playing=true; //启动计算机出牌线程和游戏者线程 AfxBeginThread(CompThread,(void*)this); AfxBeginThread(PlayerThread,(void*)this);

}voidCEg11_1View::OnFileEnd(){ //TODO:Addyourcommandhandlercodehere

exit(0

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论