C多任务与多线程编程

上传人:仙*** 文档编号:233653248 上传时间:2023-10-12 格式:PPTX 页数:85 大小:425.20KB
返回 下载 相关 举报
C多任务与多线程编程_第1页
第1页 / 共85页
C多任务与多线程编程_第2页
第2页 / 共85页
C多任务与多线程编程_第3页
第3页 / 共85页
点击查看更多>>
资源描述
第1616章 多任务与多线程编程16.1 16.1 程序、进程和线程概述16.2 16.2 线程的种类16.3 16.3 线程的创建、启动和终止16.4 16.4 线程的操作和管理16.5 16.5 在VC+VC+环境中使用同步对象16.6 16.6 本章小结16.7 16.7 思考与练习第2页/共85页第1页/共85页16.1 16.1 程序、进程和线程概述16.1.1 16.1.1 多任务、进程和线程1 1Windows3.xWindows3.x的协同多任务的协同多任务如何解决后台工作和对用户的随时响应之间的协同?详见教材如何解决后台工作和对用户的随时响应之间的协同?详见教材P27P27。Windows3.x Windows3.x对应用程序的对应用程序的CPUCPU控制权的调度方式:控制权的调度方式:协同式多任务。其特点详见教材协同式多任务。其特点详见教材P27P27。补充知识:什么是模态对话框和非模态对话框?补充知识:什么是模态对话框和非模态对话框?见附带文件见附带文件1 1。第3页/共85页第2页/共85页16.Windows95/NT16.Windows95/NT的抢先式多任务的抢先式多任务 Windows95/NT Windows95/NT对应用程序的对应用程序的CPUCPU控制权的控制权的 调度方式:抢先式多任务。其特点详见教材调度方式:抢先式多任务。其特点详见教材P28P28。16.1.1 16.1.1 多任务、进程和线程第4页/共85页第3页/共85页3 3进程与线程进程与线程16.1.1 16.1.1 多任务、进程和线程 进程由私有虚拟地址空间、代码、数据和其进程由私有虚拟地址空间、代码、数据和其它操作系统资源(如进程创建的文件、同步对它操作系统资源(如进程创建的文件、同步对象等)组成。象等)组成。进程就是应用程序的运行实例。进程就是应用程序的运行实例。1)1)什么是进程?什么是进程?一个应用程序可以运行一个或多个进程。多任一个应用程序可以运行一个或多个进程。多任务就是指操作系统可以同时运行多个进程。务就是指操作系统可以同时运行多个进程。第5页/共85页第4页/共85页16.1.1 16.1.1 多任务、进程和线程2)2)什么是线程?什么是线程?一个线程可以执行程序的任意部分的代码,一个线程可以执行程序的任意部分的代码,即使这部分代码被另一个线程并发地执行。即使这部分代码被另一个线程并发地执行。线程是线程是Windows95/NTWindows95/NT操作系统分时调度中分配操作系统分时调度中分配CPUCPU时间的基本单位。时间的基本单位。第6页/共85页第5页/共85页一个进程可以有一个或多个线程,其中一个一个进程可以有一个或多个线程,其中一个是主线程。是主线程。一个进程的所有线程共享它的虚拟地址空间、一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。全局变量和操作系统资源。16.1.1 16.1.1 多任务、进程和线程3)3)进程与线程的关系?进程与线程的关系?第7页/共85页第6页/共85页16.2 16.2 线程的种类线程有两种线程有两种用户界面线程用户界面线程工作者线程工作者线程 MFC MFC应用程序通过调用应用程序通过调用AfxBeginThreadAfxBeginThread函数并给定不同的参数来自动创建两种线函数并给定不同的参数来自动创建两种线程,而不需要程序自己创建,程,而不需要程序自己创建,AfxBeginThreadAfxBeginThread函数的具体说明在函数的具体说明在16.3.116.3.1中。中。第8页/共85页第7页/共85页16.16.1 MFC16.16.1 MFC中的线程类1.1.MFC MFC应用程序中的线程可由对象应用程序中的线程可由对象CWinThreadCWinThread表示,表示,CWinThreadCWinThread类派生自类派生自CcmdTargetCcmdTarget类;类;16.16.CWinThread CWinThread对象代表在一个应用程序内运行的线程;对象代表在一个应用程序内运行的线程;3.3.CWinThread CWinThread对象允许一个应用程序拥有多个线程;对象允许一个应用程序拥有多个线程;4.4.CWinThread CWinThread对象支持两种线程类型:用户界面线程和工作者线程对象支持两种线程类型:用户界面线程和工作者线程;第9页/共85页第8页/共85页16.16.1 MFC16.16.1 MFC中的线程类5.5.用户界面线程可以由用户界面线程可以由CWinThreadCWinThread类派生,也可以是类派生,也可以是CWinAppCWinApp类或其派生类。类或其派生类。但为安全起见,应由但为安全起见,应由CWinThreadCWinThread类派生。类派生。6.6.任何使用任何使用MFCMFC的线程必须由的线程必须由MFCMFC创建,创建一个线程必须调用创建,创建一个线程必须调用AfxBeginThreadAfxBeginThread函数。函数。7.7.CWinThread CWinThread类的数据成员即成员函数见表类的数据成员即成员函数见表2-12-1。第10页/共85页第9页/共85页16.16.2 16.16.2 用户界面线程(UIUI)1)1)用户界面线程拥有自己的消息循环来处理界面消息,具用户界面线程拥有自己的消息循环来处理界面消息,具有收发消息的功能,处理从消息队列取得的消息;有收发消息的功能,处理从消息队列取得的消息;2)2)用户界面线程通常要与用户交互;用户界面线程通常要与用户交互;3)3)用户界面线程可由用户界面线程可由CWinAppCWinApp类派生类派生(注:注:CWinAppCWinApp类由类由CWinThreadCWinThread类派生类派生),也可以由,也可以由CWinThreadCWinThread类直接派生。类直接派生。4)4)一个应用程序的主线程通常由一个应用程序的主线程通常由CWinAppCWinApp类派生,主类派生,主线程应该是用户界面线程。线程应该是用户界面线程。第11页/共85页第10页/共85页16.16.3 16.16.3 工作者线程1)1)工作者线程没有自己的消息循环,一般用来完成工作者线程没有自己的消息循环,一般用来完成后台的工作,如后台计算、打印、与其它设备的串后台的工作,如后台计算、打印、与其它设备的串行数据通信等,这些工作的共同特点就是耗时。行数据通信等,这些工作的共同特点就是耗时。2)2)为了不影响主线程与用户的交互,通常耗时的工为了不影响主线程与用户的交互,通常耗时的工作交给工作者线程来完成;作交给工作者线程来完成;3)3)工作者线程可由工作者线程可由CWinThreadCWinThread类直接派生。类直接派生。第12页/共85页第11页/共85页16.3 16.3 线程的创建、启动和终止16.3.1 16.3.1 线程的创建线程的创建由线程的创建由AfxBeginThreadAfxBeginThread函数完成。函数完成。AfxBeginThread AfxBeginThread函数有两种调用格式,可以根据需要分别用来创建工作者线程和函数有两种调用格式,可以根据需要分别用来创建工作者线程和用户界面线程。用户界面线程。第13页/共85页第12页/共85页一、一、AfxBeginThreadAfxBeginThread函数用来创建工作者线程的调用格式:函数用来创建工作者线程的调用格式:16.3.1 16.3.1 线程的创建CWinThread*AfxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);第14页/共85页第13页/共85页pfnThreadProc:pfnThreadProc:线程函数的地址线程函数的地址,该参数不能设置为该参数不能设置为NULL,NULL,线程函数必须定义线程函数必须定义成全局函数或者类的静态成员函数。成全局函数或者类的静态成员函数。例如:UINTmyThreadFunc(LPVOIDlparam)或者classApublic:staticUINT_stdcallmyThreadFunc(LPVOIDlparam);1.1.参数说明参数说明:16.3.1 16.3.1 线程的创建第15页/共85页第14页/共85页 pParam:pParam:要传递给线程函数的参数;要传递给线程函数的参数;nPriority:nPriority:要启动的线程的优先级要启动的线程的优先级,默认优先级为默认优先级为THREAD_PRIORITY_NORMAL(THREAD_PRIORITY_NORMAL(普通优先级普通优先级),),关于线程优先级的详细说明见关于线程优先级的详细说明见16.4.216.4.2;nStackSize:nStackSize:新线程的堆栈大小新线程的堆栈大小,如果设置为如果设置为0,0,则使用则使用默认大小默认大小,在应用程序中一般情况下线程的默认堆栈在应用程序中一般情况下线程的默认堆栈大小为大小为1M1M;16.3.1 16.3.1 线程的创建第16页/共85页第15页/共85页lpSecurityAttrslpSecurityAttrs:指向指向SECURITY_ATTRIBUTESSECURITY_ATTRIBUTES结构的指针,结构中指定了线程结构的指针,结构中指定了线程的安全属性。如果为的安全属性。如果为NULLNULL,则与,则与创建它的线程的安全属性相同。创建它的线程的安全属性相同。dwCreateFlags:dwCreateFlags:线程创建标志线程创建标志,该参数指定线程的初始状态,它可以被指定该参数指定线程的初始状态,它可以被指定为下列标志:为下列标志:0 0:线程在创建后立即执行:线程在创建后立即执行 CREATE_SUSPENDED:CREATE_SUSPENDED:线程在创建后立即挂起线程在创建后立即挂起16.3.1 16.3.1 线程的创建第17页/共85页第16页/共85页16.16.函数返回值的说明函数返回值的说明:函数函数AfxBeginThreadAfxBeginThread返回指向返回指向CWinThreadCWinThread类的指针。类的指针。16.3.1 16.3.1 线程的创建第18页/共85页第17页/共85页3.3.创建工作者线程的过程创建工作者线程的过程:利用函数利用函数AfxBeginThreadAfxBeginThread创建工作者线程需要两步:创建工作者线程需要两步:1)1)编写线程控制函数;编写线程控制函数;2)2)调用函数调用函数AfxBeginThreadAfxBeginThread启动线程,将线程控制函数的地址作为第一个参启动线程,将线程控制函数的地址作为第一个参数,线程控制函数的参数作为数,线程控制函数的参数作为第二个参数赋给第二个参数赋给AfxBeginThreadAfxBeginThread函数,函数,16.3.1 16.3.1 线程的创建第19页/共85页第18页/共85页 在应用程序中,可以创建一个指向在应用程序中,可以创建一个指向CWinThreadCWinThread类的指针,用来保存类的指针,用来保存AfxBeginThreadAfxBeginThread函数的返回值,即函数的返回值,即AfxBeginThreadAfxBeginThread函数创建成功的线程类函数创建成功的线程类CWinThreadCWinThread,以便创建好的线程进行控制,例如:以便创建好的线程进行控制,例如:4.4.其它说明其它说明:16.3.1 16.3.1 线程的创建第20页/共85页第19页/共85页CWinThread*pWinThread;CWinThread*pWinThread;pWinThread=AfxBeginThread(pWinThread=AfxBeginThread(ControlFunction,ControlFunction,pParam,pParam,THREAD_PRIORTY_NORMAL,THREAD_PRIORTY_NORMAL,0,0,CREATE_SUSPENDED,CREATE_SUSPENDED,NULL);NULL);16.3.1 16.3.1 线程的创建第21页/共85页第20页/共85页pWinThread-m_bAutoDelete=false;deletepWinThread;这时应注意这时应注意:即要将即要将CWinThreadCWinThread类的数据成员类的数据成员m_bAutoDeletem_bAutoDelete设为设为false,false,并且在退出进程前,并且在退出进程前,将指向线程类将指向线程类CWinThreadCWinThread的指针的指针pWinThreadpWinThread删除。删除。16.3.1 16.3.1 线程的创建第22页/共85页第21页/共85页二、二、AfxBeginThreadAfxBeginThread函数用来创建用户界面线程的调用格式:函数用来创建用户界面线程的调用格式:CWinThread*AfxBeginThread(CRuntimeClass*pThreadClass,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);16.3.1 16.3.1 线程的创建第23页/共85页第22页/共85页 pfnThreadProc:pfnThreadProc:指向指向CRuntimeClassCRuntimeClass类的指针。类的指针。其它参数的说明与前面相同。其它参数的说明与前面相同。1.1.参数说明参数说明:16.16.创建用户界面线程的过程创建用户界面线程的过程:1)1)从从CWinThreadCWinThread类派生一个新类,派生类必须用类派生一个新类,派生类必须用DECLARE_DYNCREATEDECLARE_DYNCREATE和和IMPLEMENT_DYNCREATEIMPLEMENT_DYNCREATE宏来声明和实现;宏来声明和实现;16.3.1 16.3.1 线程的创建第24页/共85页第23页/共85页2)2)重载派生类的重载派生类的InitInstanceInitInstance、ExitInstanceExitInstance等函数,在等函数,在InitInstanceInitInstance函数函数中添加代码。中添加代码。四、四、通过例题演示利用通过例题演示利用AfxBeginThreadAfxBeginThread函数创建工作者线程和用户界面线函数创建工作者线程和用户界面线程的过程。程的过程。16.3.1 16.3.1 线程的创建三、三、关于关于CreateThread()CreateThread()函数的一些说明:函数的一些说明:见附带文件见附带文件2 2。第25页/共85页第24页/共85页16.3.2 16.3.2 线程的启动 线程启动时的初始状态可以通过线程启动时的初始状态可以通过AfxBeginThread()AfxBeginThread()函数的函数的dwCreateFlagsdwCreateFlags参数指定。参数指定。如下:如下:0 0:线程在创建后立即执行;:线程在创建后立即执行;CREATE_SUSPENDED:CREATE_SUSPENDED:线程在创建后立即挂起;线程在创建后立即挂起;所谓挂起就是暂停线程的执行。所谓挂起就是暂停线程的执行。第26页/共85页第25页/共85页16.3.3 16.3.3 线程的终止遇到以下情况时,线程终止执行:遇到以下情况时,线程终止执行:1.1.线程控制函数返回(即执行了线程控制函数返回(即执行了returnreturn语句)。语句)。16.16.线程自身调用函数线程自身调用函数ExitThread()ExitThread()函数函数即终止自己即终止自己。该函数的原型如下:。该函数的原型如下:VOID WINAPI ExitThread(DWORD dwExitCode);VOID WINAPI ExitThread(DWORD dwExitCode);该函数通过参数该函数通过参数dwExitCodedwExitCode给线程设置退出码后,即终止线程的执行。给线程设置退出码后,即终止线程的执行。第27页/共85页第26页/共85页16.3.3 16.3.3 线程的终止3.3.同一进程或其他进程的线程调用同一进程或其他进程的线程调用TerminateThreadTerminateThread函数,其原型为:函数,其原型为:BOOL TerminateThread(BOOL TerminateThread(HANDLE hThread,HANDLE hThread,DWORD dwExitCode DWORD dwExitCode ););该函数用来结束由该函数用来结束由hThreadhThread参数指定的线程,并把参数指定的线程,并把dwExitCodedwExitCode设成该线程的退出码。当某个线程不再响应时,设成该线程的退出码。当某个线程不再响应时,我们可以用其他线程调用该函数来终止这个不响应的线程。我们可以用其他线程调用该函数来终止这个不响应的线程。第28页/共85页第27页/共85页16.3.3 16.3.3 线程的终止4.4.包含包含线程的进程被终止,如其它进程调用线程的进程被终止,如其它进程调用TerminateProcessTerminateProcess函数终止进函数终止进程的执行,或进程自身调用程的执行,或进程自身调用ExitProcessExitProcess函数终止自身的执行。函数终止自身的执行。BOOL WINAPI TerminateProcess(BOOL WINAPI TerminateProcess(HANDLE hProcess,HANDLE hProcess,UINT uExitCode UINT uExitCode ););TerminateProcessTerminateProcess函数的原型为:函数的原型为:第29页/共85页第28页/共85页VOID WINAPI ExitProcess(UINT uExitCode);VOID WINAPI ExitProcess(UINT uExitCode);ExitProcess函数的原型为:函数的原型为:16.3.3 16.3.3 线程的终止5.5.调用全局函数调用全局函数AfxEndThreadAfxEndThread终止进程;终止进程;注意:最好使用第注意:最好使用第1 1种方式终止线程,第种方式终止线程,第2424种方式都不宜采用。种方式都不宜采用。第30页/共85页第29页/共85页16.4.1 16.4.1 线程的运行状态的设置16.4 16.4 线程的操作和管理1.1.当参数当参数dwCreateFlagsdwCreateFlags置为置为0 0时,调用时,调用AfxBeginThreadAfxBeginThread函数创建的线程一启动就立函数创建的线程一启动就立即执行。这时,如果想暂停该线程的执行,可调用其成员函数即执行。这时,如果想暂停该线程的执行,可调用其成员函数SuspendedThread()SuspendedThread()函数将自身挂起。函数将自身挂起。AfxEndThread AfxEndThread函数的函数的dwCreateFlagsdwCreateFlags参数是决定线程在创建时的运行状态的。参数是决定线程在创建时的运行状态的。第31页/共85页第30页/共85页16.4.1 16.4.1 线程的运行状态的设置16.16.当参数当参数dwCreateFlagsdwCreateFlags置为置为CREATE_SUSPENDEDCREATE_SUSPENDED时,调用时,调用AfxBeginThreadAfxBeginThread函数创函数创建的线程一启动就挂起,暂停执行。这时,如果想继续执行线程,可调用成员函建的线程一启动就挂起,暂停执行。这时,如果想继续执行线程,可调用成员函数数ResumeThread()ResumeThread()函数唤醒被挂起的线程。函数唤醒被挂起的线程。被挂起的线程不能调用此函数唤醒自身,必须由一个未被挂起的处于运行被挂起的线程不能调用此函数唤醒自身,必须由一个未被挂起的处于运行状态的线程调用此函数来取消挂起。状态的线程调用此函数来取消挂起。注意:注意:第32页/共85页第31页/共85页16.4.2 16.4.2 线程的优先级一、一、WindowsWindows操作系统是根据进程和线程的优先级来确定它们的排队顺序并分操作系统是根据进程和线程的优先级来确定它们的排队顺序并分配配CPUCPU时间的,所以对于进程和线程在其创建时要设置优先级。时间的,所以对于进程和线程在其创建时要设置优先级。二、二、线程的优先级是根据其创建时设置的优先级和拥有该线程的进程的优先级线程的优先级是根据其创建时设置的优先级和拥有该线程的进程的优先级来确定的。其最终的优先级是来确定的。其最终的优先级是0 0到到3131之间的数值,数值越大,优先级越高。其中,之间的数值,数值越大,优先级越高。其中,0 01515级是普通优先级,级是普通优先级,1616 3030级是实时优先级。级是实时优先级。第33页/共85页第32页/共85页16.4.2 16.4.2 线程的优先级WindowsWindows操作系统对具有操作系统对具有普通优先级普通优先级的线程的调度特点是:的线程的调度特点是:高优先级线程先运行,只有高优先级线程不运行时,才调度低优先级线程高优先级线程先运行,只有高优先级线程不运行时,才调度低优先级线程运行。优先级相同的线程按照时间片轮流运行。运行。优先级相同的线程按照时间片轮流运行。三、三、WindowsWindows操作系统对线程的调度特点操作系统对线程的调度特点第34页/共85页第33页/共85页16.4.2 16.4.2 线程的优先级四、四、WindowsWindows操作系统对具有实时优先级的线程的调度特点是:操作系统对具有实时优先级的线程的调度特点是:高优先级线程先运行,只有高优先级线程不运行时,才调度低优先级线程高优先级线程先运行,只有高优先级线程不运行时,才调度低优先级线程运行。优先级相同的线程不按照时间片轮转,而是先运行的线程就先控制运行。优先级相同的线程不按照时间片轮转,而是先运行的线程就先控制CPUCPU,如果它不主动放弃控制,同级或低优先级的线程就无法运行。,如果它不主动放弃控制,同级或低优先级的线程就无法运行。第35页/共85页第34页/共85页16.4.2 16.4.2 线程的优先级五、五、用函数用函数AfxBeginThreadAfxBeginThread创建线程时,其参数创建线程时,其参数nPrioritynPriority指定新线程的优先指定新线程的优先级,该参数的取值为以下七个值之一:级,该参数的取值为以下七个值之一:lTHREAD_PRIORITY_TIME_CRITICALlTHREAD_PRIORITY_HIGHEST lTHREAD_PRIORITY_ABOVE_NORMAL lTHREAD_PRIORITY_NORMALlTHREAD_PRIORITY_BELOW_NORMALlTHREAD_PRIORITY_LOWESTlTHREAD_PRIORITY_IDLE 第36页/共85页第35页/共85页16.4.2 16.4.2 线程的优先级 对这七个值的说明,及线程最终优先级的确定:对这七个值的说明,及线程最终优先级的确定:见附带文件见附带文件3 3。六、如何改变线程的优先级?六、如何改变线程的优先级?在应用程序中,如果需要改变线程的优先级,可以通过线程类的成员函数在应用程序中,如果需要改变线程的优先级,可以通过线程类的成员函数SetThreadPrioritySetThreadPriority()()对线程的优先级重新设置。其原型如下:对线程的优先级重新设置。其原型如下:BOOL SetThreadPriorityBOOL SetThreadPriority(int nPriorityint nPriority)成功执行后返回非零值,不成功返回成功执行后返回非零值,不成功返回0 0。第37页/共85页第36页/共85页16.4.2 16.4.2 线程的优先级七、如何获得线程的优先级?七、如何获得线程的优先级?在应用程序中,有时需要查询线程的优先级,这时可以通过线程类的成员函数在应用程序中,有时需要查询线程的优先级,这时可以通过线程类的成员函数GetThreadPriorityGetThreadPriority()()获得线程的优先级。其原型如下:获得线程的优先级。其原型如下:int GetThreadPriority(HANDLE hThread)int GetThreadPriority(HANDLE hThread)返回返回THREAD_PRIORITY_ERROR_RETURN THREAD_PRIORITY_ERROR_RETURN 表示失败表示失败。成功时返回优先级的七个值。成功时返回优先级的七个值。第38页/共85页第37页/共85页16.4.2 16.4.2 线程的优先级八、八、WindowsWindows动态调度线程的说明动态调度线程的说明 线程的优先级不是一直不变的,线程的优先级不是一直不变的,WindowsWindows操作系统对线程从优先级进行动态调操作系统对线程从优先级进行动态调整,以保证所有的线程都能较好的运行。整,以保证所有的线程都能较好的运行。当线程长时间挂起以等待激活它再次运行的信号时,比它优先级低的线程将当线程长时间挂起以等待激活它再次运行的信号时,比它优先级低的线程将难以得到所需的难以得到所需的CPUCPU时间。这种情况下,当如果某线程在一段时间没有运行,时间。这种情况下,当如果某线程在一段时间没有运行,WindowsWindows操作系统将提高它的优先级以保证其获得操作系统将提高它的优先级以保证其获得CPUCPU时间。时间。第39页/共85页第38页/共85页16.4.3 16.4.3 线程间的通信一、一、用简单的布尔型变量实现线程间的通信用简单的布尔型变量实现线程间的通信 见见P40P40例题例题二、二、通过消息的发送和处理来实现线程和主程序之间的通信通过消息的发送和处理来实现线程和主程序之间的通信1.1.调用调用:PostMessage():PostMessage(),其原型为:,其原型为:第40页/共85页第39页/共85页16.4.3 16.4.3 线程间的通信BOOLBOOLPostMessage(PostMessage(HWND HWND hWnd,hWnd,/要发送到的窗口的句柄要发送到的窗口的句柄 UINT UINT Msg,Msg,/消息的消息的IDID值值 WPARAM WPARAM wParam,/wParam,/消息的第一个参数消息的第一个参数 LPARAM LPARAM lParam /lParam /消息的第二个参数消息的第二个参数 ););如果执行成功返回非零值,不成功返回如果执行成功返回非零值,不成功返回0 0。第41页/共85页第40页/共85页16.4.3 16.4.3 线程间的通信实现方法:实现方法:1)1)在头文件中定义一个消息,如线程终止的消息:在头文件中定义一个消息,如线程终止的消息:const WM_THREADENDED WM_USER+10const WM_THREADENDED WM_USER+102)2)加入相应的消息处理函数的声明:加入相应的消息处理函数的声明:afx_msg LONG OnThreadendedafx_msg LONG OnThreadended(WPARAM wParamWPARAM wParam,LPARAM lParamLPARAM lParam););第42页/共85页第41页/共85页16.4.3 16.4.3 线程间的通信3)3)在实现文件中,在消息映射部分,加入消息映射:在实现文件中,在消息映射部分,加入消息映射:ON_MESSGAE(WM_THREADENDED,OnThreadendedON_MESSGAE(WM_THREADENDED,OnThreadended)4)4)在线程中使用在线程中使用PostMessage()PostMessage()函数:函数:PostMessage(HWND)pParam,WM_THREADENDED,0,0)PostMessage(HWND)pParam,WM_THREADENDED,0,0)这个语句激活相应的消息处理函数,对这个语句激活相应的消息处理函数,对WM_THREADENDEDWM_THREADENDED消息进行处消息进行处理理第43页/共85页第42页/共85页16.4.3 16.4.3 线程间的通信16.16.调用调用CWinThread:PostThreadMessage()CWinThread:PostThreadMessage(),其原型为:其原型为:BOOL PostThreadMessage(BOOL PostThreadMessage(UINT message,/UINT message,/消息的消息的IDID值值 WPARAM wParam,/WPARAM wParam,/消息的第一个参数消息的第一个参数 LPARAM lParam /LPARAM lParam /消息的第二个参数消息的第二个参数););如果执行成功返回非零值,不成功返回如果执行成功返回非零值,不成功返回0 0。第44页/共85页第43页/共85页三、三、使使用同步类来实现线程之间的通信和控制用同步类来实现线程之间的通信和控制 在在16.516.5小节中将详细介绍小节中将详细介绍16.4.3 16.4.3 线程间的通信第45页/共85页第44页/共85页16.5 16.5 在VC+VC+环境中使用同步对象16.16.一个进程的所有线程共享它的虚拟地址空间、全局一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。变量和操作系统资源。为什么要使用同步对象?为什么要使用同步对象?1.1.进程由私有虚拟地址空间、代码、数据和其它操作系进程由私有虚拟地址空间、代码、数据和其它操作系统资源(如进程创建的文件、同步对象等)组成。统资源(如进程创建的文件、同步对象等)组成。3.3.如果对多个线程之间的资源访问不加以同步控制,这如果对多个线程之间的资源访问不加以同步控制,这些线程在共享资源时,容易产生访问冲突,产生不正确些线程在共享资源时,容易产生访问冲突,产生不正确的结果。的结果。第46页/共85页第45页/共85页16.5 16.5 在VC+VC+环境中使用同步对象 例如在数据库应用程序中,需要同时存在两个线程,一个负责读数据,一个负例如在数据库应用程序中,需要同时存在两个线程,一个负责读数据,一个负责写数据。这时就要谨防两个线程同时对数据进行操作。这时如果不进行同步控制,责写数据。这时就要谨防两个线程同时对数据进行操作。这时如果不进行同步控制,读线程所读取的数据,其状态是不确定的。读线程所读取的数据,其状态是不确定的。所以当有两个或多个线程在共享数据时,要使用同步对象以确保这多个线程不所以当有两个或多个线程在共享数据时,要使用同步对象以确保这多个线程不会同时访问共享资源。会同时访问共享资源。第47页/共85页第46页/共85页16.5 16.5 在VC+VC+环境中使用同步对象CSyncObjectCEventCObjectCCriticalSectionCMutexCSemaphoreMFC中的同步类中的同步类第48页/共85页第47页/共85页16.5.1 16.5.1 事件对象 CEvent CEvent类提供了对事件的支持。事件是一个允许一个线程在某种情况发生类提供了对事件的支持。事件是一个允许一个线程在某种情况发生时,唤醒另外一个线程的同步对象。事件告诉线程何时去执行某一给定的任务,时,唤醒另外一个线程的同步对象。事件告诉线程何时去执行某一给定的任务,从而使多个线程流平滑。从而使多个线程流平滑。每一个每一个CEventCEvent对象可以有两种状态:有信号状态对象可以有两种状态:有信号状态(signaled)(signaled)和无信号状态和无信号状态(nonsignaled)(nonsignaled)。线程监视位于其中的。线程监视位于其中的CEventCEvent类对象的状态,并在相应的时候采类对象的状态,并在相应的时候采取相应的操作。取相应的操作。第49页/共85页第48页/共85页16.5.1 16.5.1 事件对象 CEventCEvent类的成员:类的成员:构造函数构造函数CEventCEvent PulseEvent PulseEvent函数函数 Unlock Unlock函数函数 ResetEvent ResetEvent函数函数 SetEvent SetEvent函数函数下面分别介绍下面分别介绍CEventCEvent类的这些成员函数。类的这些成员函数。第50页/共85页第49页/共85页16.5.1 16.5.1 事件对象1.1.构造函数构造函数CEventCEvent的原型的原型:CEvent(BOOLbInitiallyOwn,BOOLbManualReset,LPCTSTRlpszName,LPSECURITY_ATTRIBUTESlpsaAttribute)其参数说明如下:其参数说明如下:第51页/共85页第50页/共85页16.5.1 16.5.1 事件对象 bInitiallyOwn bInitiallyOwn:若若bInitiallyOwnbInitiallyOwn为为TRUETRUE,则使,则使CMultilockCMultilock类对象和类对象和CSingleLockCSingleLock类对象的线程可类对象的线程可用;否则,要访问资源的线程必须等待。该参数的默认值为用;否则,要访问资源的线程必须等待。该参数的默认值为FALSEFALSE。bManualReset bManualReset:指定要创建的指定要创建的CEventCEvent对象是属于手工事件对象还是自动事件对象。若为对象是属于手工事件对象还是自动事件对象。若为TRUETRUE,则,则为手工事件对象,否则为自动事件对象。该参数默认值为为手工事件对象,否则为自动事件对象。该参数默认值为FALSEFALSE。第52页/共85页第51页/共85页 补充说明:补充说明:在在MFCMFC中,中,CEventCEvent类对象有两种类型,分别是所谓的手工事件和类对象有两种类型,分别是所谓的手工事件和自动事件。对于自动事件,当其获得信号后,就会释放下一个可用的线程。一个自动事件。对于自动事件,当其获得信号后,就会释放下一个可用的线程。一个自动自动 CEventCEvent对象在被至少一个线程释放后会自动返回到无信号状态;而人工事对象在被至少一个线程释放后会自动返回到无信号状态;而人工事件对象获得信号后,释放所有可利用线程,直到调用成员函数件对象获得信号后,释放所有可利用线程,直到调用成员函数ReSetEvent()ReSetEvent()将其将其设置为无信号状态时为止。设置为无信号状态时为止。注意:注意:在创建在创建CEventCEvent类的对象时,默认创建的是自动事件。类的对象时,默认创建的是自动事件。16.5.1 16.5.1 事件对象第53页/共85页第52页/共85页16.5.1 16.5.1 事件对象lpszNamelpszName:指定要创建的事件对象的名字,如果该事件对象将跨进程使用,则此参数不能指定要创建的事件对象的名字,如果该事件对象将跨进程使用,则此参数不能为为NULLNULL。如果该参数和一个已经存在的。如果该参数和一个已经存在的CEventCEvent对象相同,则该对象相同,则该构造函数构造函数返回一个对返回一个对这个已存在对象的引用;如果参数和一个已存在的非这个已存在对象的引用;如果参数和一个已存在的非CEventCEvent类的同步对象类的同步对象(如如CMutex)CMutex)相同,则对象创建失败;相同,则对象创建失败;第54页/共85页第53页/共85页16.5.1 16.5.1 事件对象 lpsaAttribute lpsaAttribute:指向指向SECURITY_ATTRIBUTESSECURITY_ATTRIBUTES结构的指针,该参数决定要创建的事件对象的安全结构的指针,该参数决定要创建的事件对象的安全属性,一般置为属性,一般置为NULLNULL。第55页/共85页第54页/共85页16.5.1 16.5.1 事件对象16.BOOL SetEvent()16.BOOL SetEvent():将将CEventCEvent类对象的状态设置为有信号状态,并且释放类对象的状态设置为有信号状态,并且释放所有等待的线程;如果该事件是人工事件,则所有等待的线程;如果该事件是人工事件,则CEventCEvent类对类对象保持为有信号状态,直到调用成员函数象保持为有信号状态,直到调用成员函数ResetEvent()ResetEvent()将将其重新设为无信号状态时为止,这样该事件就可以释放多其重新设为无信号状态时为止,这样该事件就可以释放多个线程;如果个线程;如果CEventCEvent类对象为自动事件,则在类对象为自动事件,则在SetEvent()SetEvent()将事件设置为有信号状态后,将事件设置为有信号状态后,CEventCEvent类对象由系统自动重类对象由系统自动重置为无信号状态,除非一个线程被释放。置为无信号状态,除非一个线程被释放。如果该函数执行成功,则返回非零值,否则返回零。如果该函数执行成功,则返回非零值,否则返回零。第56页/共85页第55页/共85页16.5.1 16.5.1 事件对象3.BOOL ResetEvent()3.BOOL ResetEvent()该函数将事件的状态设置为无信号状态,并保持该状态直至该函数将事件的状态设置为无信号状态,并保持该状态直至SetEvent()SetEvent()被调被调用时为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。用时为止。由于自动事件是由系统自动重置,故自动事件不需要调用该函数。如果该函数执行成功,返回非零值,否则返回非零。如果该函数执行成功,返回非零值,否则返回非零。第57页/共85页第56页/共85页16.5.1 16.5.1 事件对象4.BOOL PulseEvent()4.BOOL PulseEvent()发送一个事件脉冲,该函数完成一系列操作后才返发送一个事件脉冲,该函数完成一系列操作后才返回。对于自动事件,回。对于自动事件,PulseEvent()PulseEvent()将事件设置为有信号将事件设置为有信号状态,等待一个线程被释放,将事件重置为无信号状态,状态,等待一个线程被释放,将事件重置为无信号状态,然后然后PulseEvent()PulseEvent()返回;对于人工事件,则将等待该事返回;对于人工事件,则将等待该事件的所有线程被释放,事件被自动重置为无信号状态,件的所有线程被释放,事件被自动重置为无信号状态,然后然后PulseEvent()PulseEvent()返回。返回。一个一个CEventCEvent对象在线程中被创建后,自动处于无信对象在线程中被创建后,自动处于无信号状态,但在另一个线程中可以调用号状态,但在另一个线程中可以调用Win32 API WaitForSingleObjectWin32 API WaitForSingleObject()()函数来监视其状态。函数来监视其状态。第58页/共85页第57页/共85页16.5.1 16.5.1 事件对象5.BOOL Unlock()5.BOOL Unlock()如果线程拥有事件对象,并且事件对象是一个自动事件对象,则返回非零如果线程拥有事件对象,并且事件对象是一个自动事件对象,则返回非零值,否则返回值,否则返回0。该函数用来释放事件对象,即把该函数用来释放事件对象,即把Lock锁定的线程解锁。锁定的线程解锁。第59页/共85页第58页/共85页16.5.1 16.5.1 事件对象另外补充一个函数另外补充一个函数 BOOL Lock()BOOL Lock()该函数不是该函数不是CEvent类的成员函数,是类的成员函数,是CEvent的父类的父类CSyncObject类的成员类的成员函数,函数,CEvent通过继承可以使用该函数。通过继承可以使用该函数。该函数用来锁定事件对象,阻止线程的继续执行,直到事件对象处于活动状该函数用来锁定事件对象,阻止线程的继续执行,直到事件对象处于活动状态为止。态为止。第60页/共85页第59页/共85页6.WaitForSingleObject():其原型声明如下:其原型声明如下:DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds)hHandle hHandle为指向要监视的同步对象的句柄;为指向要监视的同步对象的句柄;dwMilliseconds dwMilliseconds为监视为监视hHandlehHandle所指向的对象所设置的超时值,单位为毫秒。所指向的对象所设置的超时值,单位为毫秒。16.5.1 16.5.1 事件对象第61页/共85页第60页/共85页 当在线程的执行函数中调用该函数时,线程暂时挂当在线程的执行函数中调用该函数时,线程暂时挂起,系统监视起,系统监视hHandlehHandle所指向的对象的状态。如果经过所指向的对象的状态。如果经过dwMillisecondsdwMilliseconds毫秒后,毫秒后,hHandlehHandle指向的对象变为有信号指向的对象变为有信号状态,则状态,则WaitForSingleObject()WaitForSingleObject()返回,线程被释放,且返回,线程被释放,且返回值为返回值为WAIT_TIMEOUTWAIT_TIMEOUT;如果在挂起的;如果在挂起的dwMillisecondsdwMilliseconds毫秒内,线程所等待的对象在某一时刻变为有信号,则毫秒内,线程所等待的对象在某一时刻变为有信号,则该函数立即返回,返回值为该函数立即返回,返回值为WAIT_OBJECT_0WAIT_OBJECT_0。16.5.1 16.5.1 事件对象第62页/共85页第61页/共85页 参数参数dwMillisecondsdwMilliseconds有两个具有特殊意义的值:有两个具有特殊意义的值:0 0和和INFINITEINFINITE。若为。若为0 0,则该函,则该函数立即返回;若为数立即返回;若为INFINITEINFINITE,则线程一直被挂起,直到,则线程一直被挂起,直到hHandlehHandle所指向的对象变为所指向的对象变为有信号状态时为止。有信号状态时为止。16.5.1 16.5.1 事件对象第63页/共85页第62页/共85页 CEvent:ResetEvent()CEvent:ResetEvent()把对象设置为无信号状态,程把对象设置为无信号状态,程序在序在WaitForSingleObject(hHandle,INFINITE)WaitForSingleObject(hHandle,INFINITE)处等待。处等待。CEvent:SetEvent()CEvent:SetEvent()把对象设置为有信号状态,释放把对象设置为有信号状态,释放等待的线程。等待的线程。如果如果CEventCEvent对象为自动事件,则当对象为自动事件,则当WaitForSingleObject(hHandle,INFINITE)WaitForSingleObject(hHandle,INFINITE)返回时,自动返回时,自动把把CEventCEvent对象重置为无信号状态。对象重置为无信号状态。16.5.1 16.5.1 事件对象总结以上,几个函数的使用顺序为:总结以上,几个函数的使用顺序为:第64页/共85页第63页/共85页 B B线程在执行到线程在执行到CEventCEvent类成员函数类成员函数Lock()Lock()时将会发生阻塞,而时将会发生阻塞,而A A线程此时则可以线程此时则可以在没有在没有B B线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员函数SetEventSetEvent()()向向B B发出事件,使其被释放,得以对发出事件,使其被释放,得以对A A先前已处理完毕的共享资源进行操作。先前已处理完毕的共享资源进行操作。16.5.1 16.5.1 事件对象另外通过一个例题来演示事件的工作原理:另外通过一个例题来演示事件的工作原理:第65页/共85页第64页/共85页16.5.2 16.5.2 临界区 临界区(临界区(Critical SectionCritical Section)是一段代码)是一段代码,该代码独占对某些共享资源的访问,该代码独占对某些共享资源的访问,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。资源的目的。第66页/共85页第65页/共85页16.5.2 16.5.2 临界区 在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响。程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他定程度上影响。程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。引起其他线程的长时间等待。第67页/共85页第66页/共85页16.5.2 16.5.2 临界区 虽然临界区同步速度很快,但却虽然临界区同步速度很快,但却只能用来同步本进程内的线程只能用来同步本进程内的线程,而不可用来同步,而不可用来同步多个进程中的线程。多个进程中的线程。MFC MFC为临界区提供有一个为临界区提供有一个CCriticalSectionCCriticalSection类,使用该类进行线程同步处理是非类,使用该类进行线程同步处理是非常简单的,只需在线程函数中用常简单的,只需
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!