




已阅读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 BOOLbInitialCountLONGlMaximumCount 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 Addyourcommandhandlercodeherem playing true 启动计算机出牌线程和游戏者线程AfxBeginThread CompThread void this AfxBeginThread PlayerThread void this voidCEg11 1View OnFileEnd TODO Addyourcommandhandlercode
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年北交所招聘面试高频题库解析
- 2025届德阳市罗江区中考数学押题试卷含解析
- 2025年宠物训导师笔试重点复习题集
- 2025年妇联笔试高频题解析
- 投资合作协议细则
- 2025年高空作业登高架设考试试题及解析
- 2025年植保无人机面试高频问题集
- 2025年滑雪中级指导员考试要点与模拟题
- 2025年安全生产安全操作规程试题集
- 2025年品质检测员执业考试试题及答案解析
- 2025年电气系统故障排查与维修技能考核试卷及答案(全新)
- 模拟联合国社团课件
- 2025-2026学年统编版(2024)小学语文二年级上册教学计划及进度表
- 2025湖南湘潭湘乡市融媒体中心招聘事业单位工作人员10人笔试备考题库及答案解析
- 县级医院骨科发展路径规划
- 健康管理师二级《理论知识》模拟考试试卷附答案
- 2025湖南省全日制用工劳动合同书
- 食品合规管理课件
- 疼痛健康教育
- 羊驼介绍课件
- 全科医学病例讨论
评论
0/150
提交评论