C++课件多任务与多线程编程

上传人:m**** 文档编号:589842753 上传时间:2024-09-11 格式:PPT 页数:84 大小:1.76MB
返回 下载 相关 举报
C++课件多任务与多线程编程_第1页
第1页 / 共84页
C++课件多任务与多线程编程_第2页
第2页 / 共84页
C++课件多任务与多线程编程_第3页
第3页 / 共84页
C++课件多任务与多线程编程_第4页
第4页 / 共84页
C++课件多任务与多线程编程_第5页
第5页 / 共84页
点击查看更多>>
资源描述

《C++课件多任务与多线程编程》由会员分享,可在线阅读,更多相关《C++课件多任务与多线程编程(84页珍藏版)》请在金锄头文库上搜索。

1、C+课件多任务与多线课件多任务与多线程编程程编程16.1 16.1 程序、进程和线程概程序、进程和线程概述述16.1.1 16.1.1 多任务、进程和线多任务、进程和线程程1 1Windows3.xWindows3.x的协同多任务的协同多任务如何解决后台工作和对用户的随时响应之间的如何解决后台工作和对用户的随时响应之间的协同?详见教材协同?详见教材P27P27。 Windows3.x Windows3.x对应用程序的对应用程序的CPUCPU控制权的调度方式:控制权的调度方式: 协同式多任务。其特点详见教材协同式多任务。其特点详见教材P27P27。 补充知识:什么是模态对话框和非模态对话框?补充

2、知识:什么是模态对话框和非模态对话框? 见附带文件见附带文件1 1。16.Windows95/NT16.Windows95/NT的抢先式多任务的抢先式多任务 Windows95/NT Windows95/NT对应用程序的对应用程序的CPUCPU控制权的控制权的 调度方式:抢先式多任务。其特点详见教材调度方式:抢先式多任务。其特点详见教材P28P28。16.1.1 16.1.1 多任务、进程和线程多任务、进程和线程3 3进程与线程进程与线程16.1.1 16.1.1 多任务、进程和线多任务、进程和线程程 进程由私有虚拟地址空间、代码、数据和其进程由私有虚拟地址空间、代码、数据和其它操作系统资源(

3、如进程创建的文件、同步对它操作系统资源(如进程创建的文件、同步对象等)组成。象等)组成。 进程就是应用程序的运行实例。进程就是应用程序的运行实例。1)1)什么是进程?什么是进程? 一个应用程序可以运行一个或多个进程。多任一个应用程序可以运行一个或多个进程。多任务就是指操作系统可以同时运行多个进程。务就是指操作系统可以同时运行多个进程。 16.1.1 16.1.1 多任务、进程和线多任务、进程和线程程2)2)什么是线程?什么是线程? 一个线程可以执行程序的任意部分的代码,一个线程可以执行程序的任意部分的代码,即使这部分代码被另一个线程并发地执行。即使这部分代码被另一个线程并发地执行。 线程是线程

4、是Windows95/NTWindows95/NT操作系统分时调度中分配操作系统分时调度中分配CPUCPU时间的基本单位。时间的基本单位。一个进程可以有一个或多个线程,其中一个一个进程可以有一个或多个线程,其中一个是主线程。是主线程。 一个进程的所有线程共享它的虚拟地址空间、一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。全局变量和操作系统资源。16.1.1 16.1.1 多任务、进程和线多任务、进程和线程程3)3)进程与线程的关系?进程与线程的关系?16.2 16.2 线程的种类线程的种类线程有两种线程有两种用户界面线程用户界面线程工作者线程工作者线程 MFC MFC应用程序

5、通过调用应用程序通过调用AfxBeginThreadAfxBeginThread函数并给定函数并给定不同的参数来自动创建两种线程,而不需要程序自己不同的参数来自动创建两种线程,而不需要程序自己创建,创建, AfxBeginThreadAfxBeginThread函数的具体说明在函数的具体说明在16.3.116.3.1中。中。16.16.1 MFC16.16.1 MFC中的线程中的线程类类1.1. MFC MFC应用程序中的线程可由对象应用程序中的线程可由对象CWinThreadCWinThread表示,表示,CWinThreadCWinThread类派生自类派生自CcmdTargetCcmdT

6、arget类;类;16.16. CWinThread CWinThread对象代表在一个应用程序内运行的线程;对象代表在一个应用程序内运行的线程;3.3. CWinThread CWinThread对象允许一个应用程序拥有多个线程;对象允许一个应用程序拥有多个线程;4.4. CWinThread CWinThread对象支持两种线程类型:用户界面线程对象支持两种线程类型:用户界面线程和工作者线程和工作者线程; ;16.16.1 MFC16.16.1 MFC中的线程中的线程类类5.5. 用户界面线程可以由用户界面线程可以由CWinThreadCWinThread类派生,也类派生,也可以是可以是C

7、WinAppCWinApp类或其派生类。但为安全起见,类或其派生类。但为安全起见,应由应由CWinThreadCWinThread类派生。类派生。6.6. 任何使用任何使用MFCMFC的线程必须由的线程必须由MFCMFC创建,创建一个创建,创建一个线程必须调用线程必须调用AfxBeginThreadAfxBeginThread函数。函数。7.7. CWinThread CWinThread类的数据成员即成员函数见表类的数据成员即成员函数见表2-12-1。16.16.2 16.16.2 用户界面线程用户界面线程(UIUI)1)1) 用户界面线程拥有自己的消息循环来处理界面消息,具用户界面线程拥有

8、自己的消息循环来处理界面消息,具有收发消息的功能,处理从消息队列取得的消息;有收发消息的功能,处理从消息队列取得的消息;2)2) 用户界面线程通常要与用户交互;用户界面线程通常要与用户交互;3)3) 用户界面线程可由用户界面线程可由CWinAppCWinApp类派生类派生( (注:注:CWinAppCWinApp类由类由CWinThreadCWinThread类派生类派生) ),也可以由,也可以由CWinThreadCWinThread类直接派生。类直接派生。4)4) 一个应用程序的主线程通常由一个应用程序的主线程通常由CWinAppCWinApp类派生,主类派生,主线程应该是用户界面线程。线

9、程应该是用户界面线程。16.16.3 16.16.3 工作者线工作者线程程1)1) 工作者线程没有自己的消息循环,一般用来完成工作者线程没有自己的消息循环,一般用来完成后台的工作,如后台计算、打印、与其它设备的串后台的工作,如后台计算、打印、与其它设备的串行数据通信等,这些工作的共同特点就是耗时。行数据通信等,这些工作的共同特点就是耗时。2)2) 为了不影响主线程与用户的交互,通常耗时的工为了不影响主线程与用户的交互,通常耗时的工作交给工作者线程来完成;作交给工作者线程来完成;3)3) 工作者线程可由工作者线程可由CWinThreadCWinThread类直接派生。类直接派生。16.3 16.

10、3 线程的创建、启动和终止线程的创建、启动和终止16.3.1 16.3.1 线程的创建线程的创建线程的创建由线程的创建由AfxBeginThreadAfxBeginThread函数完成。函数完成。 AfxBeginThread AfxBeginThread函数有两种调用格式,可以根据函数有两种调用格式,可以根据需要分别用来创建工作者线程和用户界面线程。需要分别用来创建工作者线程和用户界面线程。一、一、AfxBeginThreadAfxBeginThread函数用来创建工作者线程的函数用来创建工作者线程的调用格式:调用格式:16.3.1 16.3.1 线程的创建线程的创建CWinThread*A

11、fxBeginThread(AFX_THREADPROCpfnThreadProc,LPVOIDpParam,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);pfnThreadProc:pfnThreadProc:线程函数的地址线程函数的地址, ,该参数不能设该参数不能设置为置为NULL,NULL,线程函数必须定义成全局函数或者类线程函数必须定义成全局函数或者类的静态成员函数。的静态成员函数。例如例如:UINT

12、myThreadFunc(LPVOIDlparam)或者或者classApublic:staticUINT_stdcallmyThreadFunc(LPVOIDlparam);1.1.参数说明参数说明: :16.3.1 16.3.1 线程的创线程的创建建 pParam: pParam:要传递给线程函数的参数;要传递给线程函数的参数;nPriority:nPriority:要启动的线程的优先级要启动的线程的优先级, ,默认优先级默认优先级为为THREAD_PRIORITY_NORMAL(THREAD_PRIORITY_NORMAL(普通优先级普通优先级),),关于关于线程优先级的详细说明见线程优

13、先级的详细说明见16.4.216.4.2;nStackSize:nStackSize:新线程的堆栈大小新线程的堆栈大小, ,如果设置为如果设置为0,0,则使用则使用默认大小默认大小, ,在应用程序中一般情况下线程的默认堆栈在应用程序中一般情况下线程的默认堆栈大小为大小为1M1M;16.3.1 16.3.1 线程的创线程的创建建lpSecurityAttrslpSecurityAttrs : :指向指向SECURITY_ATTRIBUTESSECURITY_ATTRIBUTES结构结构的指针,结构中指定了线程的安全属性。如果为的指针,结构中指定了线程的安全属性。如果为NULLNULL,则与,则与

14、创建它的线程的安全属性相同。创建它的线程的安全属性相同。dwCreateFlags:dwCreateFlags:线程创建标志线程创建标志, ,该参数指定线程该参数指定线程的初始状态,它可以被指定为下列标志:的初始状态,它可以被指定为下列标志: 0 0:线程在创建后立即执行:线程在创建后立即执行 CREATE_SUSPENDED:CREATE_SUSPENDED:线程在创建后立即挂起线程在创建后立即挂起16.3.1 16.3.1 线程的创线程的创建建16.16.函数返回值的说明函数返回值的说明: :函数函数AfxBeginThreadAfxBeginThread返回指向返回指向CWinThrea

15、dCWinThread类的指针。类的指针。16.3.1 16.3.1 线程的创线程的创建建3.3.创建工作者线程的过程创建工作者线程的过程: :利用函数利用函数AfxBeginThreadAfxBeginThread创建工作者线程需要两步:创建工作者线程需要两步:1) 1) 编写线程控制函数;编写线程控制函数;2) 2) 调用函数调用函数AfxBeginThreadAfxBeginThread启动线程,将线程控启动线程,将线程控制函数的地址作为第一个参数,线程控制函数的参制函数的地址作为第一个参数,线程控制函数的参数作为数作为第二个参数赋给第二个参数赋给AfxBeginThreadAfxBeg

16、inThread函数,函数,16.3.1 16.3.1 线程的创线程的创建建 在应用程序中,可以创建一个指向在应用程序中,可以创建一个指向CWinThreadCWinThread类类的指针,用来保存的指针,用来保存AfxBeginThreadAfxBeginThread函数的返回值,即函数的返回值,即AfxBeginThreadAfxBeginThread函数创建成功的线程类函数创建成功的线程类CWinThreadCWinThread,以便创建好的线程进行控制,例如:以便创建好的线程进行控制,例如:4.4.其它说明其它说明: :16.3.1 16.3.1 线程的创线程的创建建CWinThrea

17、d* 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 线程的创线程的创建建pWinThread-m_bAutoDelete=false;delet

18、epWinThread;这时应注意这时应注意: : 即要将即要将CWinThreadCWinThread类的数据成员类的数据成员m_bAutoDeletem_bAutoDelete设为设为false,false,并且在退出进程前,将指向线程类并且在退出进程前,将指向线程类CWinThreadCWinThread的指针的指针pWinThreadpWinThread删除。删除。16.3.1 16.3.1 线程的创线程的创建建二、二、AfxBeginThreadAfxBeginThread函数用来创建用户界面线程函数用来创建用户界面线程的调用格式:的调用格式:CWinThread*AfxBeginT

19、hread(CRuntimeClass*pThreadClass,intnPriority=THREAD_PRIORITY_NORMAL,UINTnStackSize=0,DWORDdwCreateFlags=0,LPSECURITY_ATTRIBUTESlpSecurityAttrs=NULL);16.3.1 16.3.1 线程的创线程的创建建 pfnThreadProc: pfnThreadProc: 指向指向CRuntimeClassCRuntimeClass类的指针。类的指针。 其它参数的说明与前面相同。其它参数的说明与前面相同。1.1.参数说明参数说明: :16.16.创建用户界面线

20、程的过程创建用户界面线程的过程: :1) 1) 从从CWinThreadCWinThread类派生一个新类,派生类必须类派生一个新类,派生类必须用用DECLARE_DYNCREATEDECLARE_DYNCREATE和和IMPLEMENT_DYNCREATEIMPLEMENT_DYNCREATE宏宏来声明和实现;来声明和实现;16.3.1 16.3.1 线程的创线程的创建建2) 2) 重载派生类的重载派生类的InitInstanceInitInstance、ExitInstanceExitInstance等等函数,在函数,在InitInstanceInitInstance函数中添加代码。函数中

21、添加代码。四、四、通过例题演示利用通过例题演示利用AfxBeginThreadAfxBeginThread函数创建函数创建工作者线程和用户界面线程的过程。工作者线程和用户界面线程的过程。16.3.1 16.3.1 线程的创线程的创建建三、三、关于关于CreateThread( )CreateThread( )函数的一些说明:函数的一些说明: 见附带文件见附带文件2 2。16.3.2 16.3.2 线程的启动线程的启动 线程启动时的初始状态可以通过线程启动时的初始状态可以通过AfxBeginThread( )AfxBeginThread( )函函数的数的dwCreateFlagsdwCreate

22、Flags参数指定。如下:参数指定。如下:0 0:线程在创建后立即执行;:线程在创建后立即执行;CREATE_SUSPENDED:CREATE_SUSPENDED:线程在创建后立即挂起;线程在创建后立即挂起;所谓挂起就是暂停线程的执行。所谓挂起就是暂停线程的执行。16.3.3 16.3.3 线程的终线程的终止止遇到以下情况时,线程终止执行:遇到以下情况时,线程终止执行:1.1.线程控制函数返回(即执行了线程控制函数返回(即执行了returnreturn语句)。语句)。16.16.线程自身调用函数线程自身调用函数ExitThread( )ExitThread( )函数函数即终止自己即终止自己。该

23、函数的原型如下:该函数的原型如下: VOID WINAPI ExitThread(DWORD dwExitCode );VOID WINAPI ExitThread(DWORD dwExitCode ); 该函数通过参数该函数通过参数dwExitCodedwExitCode给线程设置退出码后,给线程设置退出码后,即终止线程的执行。即终止线程的执行。16.3.3 16.3.3 线程的终线程的终止止3.3.同一进程或其他进程的线程调用同一进程或其他进程的线程调用TerminateThreadTerminateThread函数,其原型为:函数,其原型为: BOOL TerminateThread(

24、BOOL TerminateThread( HANDLE hThread, HANDLE hThread, DWORD dwExitCode DWORD dwExitCode ); ); 该函数用来结束由该函数用来结束由hThreadhThread参数指定的线程,并把参数指定的线程,并把dwExitCodedwExitCode设成该线程的退出码。当某个线程不再响应时,设成该线程的退出码。当某个线程不再响应时,我们可以用其他线程调用该函数来终止这个不响应的线程。我们可以用其他线程调用该函数来终止这个不响应的线程。 16.3.3 16.3.3 线程的终线程的终止止4.4.包含包含线程的进程被终止,

25、如其它进程调用线程的进程被终止,如其它进程调用TerminateProcessTerminateProcess函数终止进程的执行,或进函数终止进程的执行,或进程自身调用程自身调用ExitProcessExitProcess函数终止自身的执行。函数终止自身的执行。BOOL WINAPI TerminateProcess(BOOL WINAPI TerminateProcess( HANDLE hProcess, HANDLE hProcess, UINT uExitCode UINT uExitCode ); ); TerminateProcessTerminateProcess函数的原型为:函

26、数的原型为:VOID WINAPI ExitProcess( UINT uExitCode ); VOID WINAPI ExitProcess( UINT uExitCode ); ExitProcess函数的原型为:函数的原型为:16.3.3 16.3.3 线程的终线程的终止止5.5. 调用全局函数调用全局函数AfxEndThreadAfxEndThread终止进程;终止进程;注意:最好使用第注意:最好使用第1 1种方式终止线程,第种方式终止线程,第2424种种方式都不宜采用。方式都不宜采用。16.4.1 16.4.1 线程的运行状态的设置线程的运行状态的设置16.4 16.4 线程的操作

27、和管理线程的操作和管理1.1.当参数当参数dwCreateFlagsdwCreateFlags置为置为0 0时,调用时,调用AfxBeginThreadAfxBeginThread函数创建的线程一启动就立即执行。函数创建的线程一启动就立即执行。这时,如果想暂停该线程的执行,可调用其成员函数这时,如果想暂停该线程的执行,可调用其成员函数SuspendedThreadSuspendedThread( )( )函数将自身挂起。函数将自身挂起。 AfxEndThread AfxEndThread函数的函数的dwCreateFlagsdwCreateFlags参数是决定参数是决定线程在创建时的运行状态的

28、。线程在创建时的运行状态的。16.4.1 16.4.1 线程的运行状态的设置线程的运行状态的设置16.16.当参数当参数dwCreateFlagsdwCreateFlags置为置为CREATE_SUSPENDEDCREATE_SUSPENDED时,时,调用调用AfxBeginThreadAfxBeginThread函数创建的线程一启动就挂起,函数创建的线程一启动就挂起,暂停执行。这时,如果想继续执行线程,可调用成暂停执行。这时,如果想继续执行线程,可调用成员函数员函数ResumeThread( )ResumeThread( )函数唤醒被挂起的线程。函数唤醒被挂起的线程。 被挂起的线程不能调用此

29、函数唤醒自身,必须被挂起的线程不能调用此函数唤醒自身,必须由一个未被挂起的处于运行状态的线程调用此函数由一个未被挂起的处于运行状态的线程调用此函数来取消挂起。来取消挂起。注意:注意:16.4.2 16.4.2 线程的优先线程的优先级级一、一、 WindowsWindows操作系统是根据进程和线程的优先级操作系统是根据进程和线程的优先级来确定它们的排队顺序并分配来确定它们的排队顺序并分配CPUCPU时间的,所以对时间的,所以对于进程和线程在其创建时要设置优先级。于进程和线程在其创建时要设置优先级。二、二、 线程的优先级是根据其创建时设置的优先级和线程的优先级是根据其创建时设置的优先级和拥有该线程

30、的进程的优先级来确定的。其最终的优拥有该线程的进程的优先级来确定的。其最终的优先级是先级是0 0到到3131之间的数值,数值越大,优先级越高。之间的数值,数值越大,优先级越高。其中,其中,0 01515级是普通优先级,级是普通优先级,1616 3030级是实时优级是实时优先级。先级。16.4.2 16.4.2 线程的优先线程的优先级级WindowsWindows操作系统对具有操作系统对具有普通优先级普通优先级的线程的调度的线程的调度特点是:特点是: 高优先级线程先运行,只有高优先级线程不运高优先级线程先运行,只有高优先级线程不运行时,才调度低优先级线程运行。优先级相同的线行时,才调度低优先级线

31、程运行。优先级相同的线程按照时间片轮流运行。程按照时间片轮流运行。三、三、WindowsWindows操作系统对线程的调度特点操作系统对线程的调度特点16.4.2 16.4.2 线程的优先线程的优先级级四、四、WindowsWindows操作系统对具有实时优先级的线程的操作系统对具有实时优先级的线程的调度特点是:调度特点是: 高优先级线程先运行,只有高优先级线程不运高优先级线程先运行,只有高优先级线程不运行时,才调度低优先级线程运行。优先级相同的线行时,才调度低优先级线程运行。优先级相同的线程不按照时间片轮转,而是先运行的线程就先控制程不按照时间片轮转,而是先运行的线程就先控制CPUCPU,如

32、果它不主动放弃控制,同级或低优先级的,如果它不主动放弃控制,同级或低优先级的线程就无法运行。线程就无法运行。16.4.2 16.4.2 线程的优先线程的优先级级五、五、 用函数用函数AfxBeginThreadAfxBeginThread创建线程时,其参数创建线程时,其参数nPrioritynPriority指定新线程的优先级,该参数的取值为指定新线程的优先级,该参数的取值为以下七个值之一:以下七个值之一:lTHREAD_PRIORITY_TIME_CRITICALlTHREAD_PRIORITY_HIGHEST lTHREAD_PRIORITY_ABOVE_NORMAL lTHREAD_PR

33、IORITY_NORMALlTHREAD_PRIORITY_BELOW_NORMALlTHREAD_PRIORITY_LOWESTlTHREAD_PRIORITY_IDLE 16.4.2 16.4.2 线程的优先线程的优先级级 对这七个值的说明,及线程最终优先级的确定:对这七个值的说明,及线程最终优先级的确定:见附带文件见附带文件3 3。六、如何改变线程的优先级?六、如何改变线程的优先级? 在应用程序中,如果需要改变线程的优先级,可在应用程序中,如果需要改变线程的优先级,可以通过线程类的成员函数以通过线程类的成员函数SetThreadPriority( )SetThreadPriority(

34、)对线对线程的优先级重新设置。其原型如下:程的优先级重新设置。其原型如下: BOOL SetThreadPriorityBOOL SetThreadPriority(int nPriorityint nPriority) 成功执行后返回非零值,不成功返回成功执行后返回非零值,不成功返回0 0。 16.4.2 16.4.2 线程的优先线程的优先级级七、如何获得线程的优先级?七、如何获得线程的优先级? 在应用程序中,有时需要查询线程的优先级,这在应用程序中,有时需要查询线程的优先级,这时可以通过线程类的成员函数时可以通过线程类的成员函数GetThreadPriority( )GetThreadPr

35、iority( )获得线程的优先级。其原型如下:获得线程的优先级。其原型如下: int GetThreadPriority( HANDLE hThread )int GetThreadPriority( HANDLE hThread ) 返回返回THREAD_PRIORITY_ERROR_RETURN THREAD_PRIORITY_ERROR_RETURN 表示失败表示失败。成功时返回优先级的七个值。成功时返回优先级的七个值。 16.4.2 16.4.2 线程的优先线程的优先级级八、八、WindowsWindows动态调度线程的说明动态调度线程的说明 线程的优先级不是一直不变的,线程的优先级

36、不是一直不变的,WindowsWindows操作系操作系统对线程从优先级进行动态调整,以保证所有的线统对线程从优先级进行动态调整,以保证所有的线程都能较好的运行。程都能较好的运行。 当线程长时间挂起以等待激活它再次运行的信当线程长时间挂起以等待激活它再次运行的信号时,比它优先级低的线程将难以得到所需的号时,比它优先级低的线程将难以得到所需的CPUCPU时时间。这种情况下,当如果某线程在一段时间没有运间。这种情况下,当如果某线程在一段时间没有运行,行,WindowsWindows操作系统将提高它的优先级以保证其获操作系统将提高它的优先级以保证其获得得CPUCPU时间。时间。16.4.3 16.4

37、.3 线程间的通线程间的通信信一、一、用简单的布尔型变量实现线程间的通信用简单的布尔型变量实现线程间的通信 见见P40P40例题例题二、二、通过消息的发送和处理来实现线程和主程序通过消息的发送和处理来实现线程和主程序之间的通信之间的通信1.1.调用调用:PostMessage( ):PostMessage( ),其原型为:,其原型为:16.4.3 16.4.3 线程间的通线程间的通信信BOOLBOOLPostMessage(PostMessage( HWND HWND hWnd, hWnd, /要发送到的窗口的句柄要发送到的窗口的句柄 UINT UINT Msg, Msg, / /消息的消息的

38、IDID值值 WPARAM WPARAM wParam, / wParam, /消息的第一个参数消息的第一个参数 LPARAM LPARAM lParam / lParam /消息的第二个参数消息的第二个参数 ); ); 如果执行成功返回非零值,不成功返回如果执行成功返回非零值,不成功返回0 0。16.4.3 16.4.3 线程间的通线程间的通信信实现方法:实现方法:1)1)在头文件中定义一个消息,如线程终止的消息:在头文件中定义一个消息,如线程终止的消息: const WM_THREADENDED WM_USER+10const WM_THREADENDED WM_USER+102)2)加入

39、相应的消息处理函数的声明:加入相应的消息处理函数的声明: afx_msg LONG OnThreadendedafx_msg LONG OnThreadended( WPARAM wParamWPARAM wParam, LPARAM lParamLPARAM lParam););16.4.3 16.4.3 线程间的通线程间的通信信3)3)在实现文件中,在消息映射部分,加入消息映射:在实现文件中,在消息映射部分,加入消息映射: ON_MESSGAE(WM_THREADENDED,OnThreadendedON_MESSGAE(WM_THREADENDED,OnThreadended)4)4)在

40、线程中使用在线程中使用PostMessage( )PostMessage( )函数:函数: PostMessage(HWND)pParam,WM_THREADENDED,0,0)PostMessage(HWND)pParam,WM_THREADENDED,0,0) 这个语句激活相应的消息处理函数,对这个语句激活相应的消息处理函数,对WM_THREADENDEDWM_THREADENDED消息进行处理消息进行处理16.4.3 16.4.3 线程间的通线程间的通信信16.16.调用调用CWinThread:PostThreadMessage( )CWinThread:PostThreadMessa

41、ge( ), 其原型为:其原型为:BOOL PostThreadMessage( BOOL PostThreadMessage( UINT message, / UINT message, /消息的消息的IDID值值 WPARAM wParam, / WPARAM wParam, /消息的第一个参数消息的第一个参数 LPARAM lParam / LPARAM lParam /消息的第二个参数消息的第二个参数); ); 如果执行成功返回非零值,不成功返回如果执行成功返回非零值,不成功返回0 0。三、三、使使用同步类来实现线程之间的通信和控制用同步类来实现线程之间的通信和控制 在在16.516.

42、5小节中将详细介绍小节中将详细介绍16.4.3 16.4.3 线程间的通线程间的通信信16.5 16.5 在在VC+VC+环境中使用同步对象环境中使用同步对象16.16.一个进程的所有线程共享它的虚拟地址空间、全局一个进程的所有线程共享它的虚拟地址空间、全局变量和操作系统资源。变量和操作系统资源。为什么要使用同步对象?为什么要使用同步对象?1.1.进程由私有虚拟地址空间、代码、数据和其它操作系进程由私有虚拟地址空间、代码、数据和其它操作系统资源(如进程创建的文件、同步对象等)组成。统资源(如进程创建的文件、同步对象等)组成。 3.3.如果对多个线程之间的资源访问不加以同步控制,这如果对多个线程

43、之间的资源访问不加以同步控制,这些线程在共享资源时,容易产生访问冲突,产生不正确些线程在共享资源时,容易产生访问冲突,产生不正确的结果。的结果。16.5 16.5 在在VC+VC+环境中使用同步对象环境中使用同步对象 例如在数据库应用程序中,需要同时存在两个线例如在数据库应用程序中,需要同时存在两个线程,一个负责读数据,一个负责写数据。这时就要谨程,一个负责读数据,一个负责写数据。这时就要谨防两个线程同时对数据进行操作。这时如果不进行同防两个线程同时对数据进行操作。这时如果不进行同步控制,读线程所读取的数据,其状态是不确定的。步控制,读线程所读取的数据,其状态是不确定的。 所以当有两个或多个线

44、程在共享数据时,要使用所以当有两个或多个线程在共享数据时,要使用同步对象以确保这多个线程不会同时访问共享资源。同步对象以确保这多个线程不会同时访问共享资源。16.5 16.5 在在VC+VC+环境中使用同步对象环境中使用同步对象CSyncObjectCEventCObjectCCriticalSectionCMutexCSemaphoreMFC中的同步类中的同步类16.5.1 16.5.1 事件对象事件对象 CEvent CEvent类提供了对事件的支持。事件是一个允类提供了对事件的支持。事件是一个允许一个线程在某种情况发生时,唤醒另外一个线程许一个线程在某种情况发生时,唤醒另外一个线程的同步

45、对象。事件告诉线程何时去执行某一给定的的同步对象。事件告诉线程何时去执行某一给定的任务,从而使多个线程流平滑。任务,从而使多个线程流平滑。 每一个每一个CEventCEvent对象可以有两种状态:有信号状对象可以有两种状态:有信号状态态(signaled)(signaled)和无信号状态和无信号状态(nonsignaled)(nonsignaled)。线程监。线程监视位于其中的视位于其中的CEventCEvent类对象的状态,并在相应的时类对象的状态,并在相应的时候采取相应的操作。候采取相应的操作。16.5.1 16.5.1 事件对象事件对象 CEventCEvent类的成员:类的成员: 构造

46、函数构造函数CEventCEvent PulseEvent PulseEvent函数函数 Unlock Unlock函数函数 ResetEvent ResetEvent函数函数 SetEvent SetEvent函数函数下面分别介绍下面分别介绍CEventCEvent类的这些成员函数。类的这些成员函数。16.5.1 16.5.1 事件对象事件对象1.1.构造函数构造函数CEventCEvent的原型的原型: :CEvent(BOOLbInitiallyOwn,BOOLbManualReset,LPCTSTRlpszName,LPSECURITY_ATTRIBUTESlpsaAttribute)

47、其参数说明如下:其参数说明如下:16.5.1 16.5.1 事件对象事件对象 bInitiallyOwn bInitiallyOwn: 若若bInitiallyOwnbInitiallyOwn为为TRUETRUE,则使,则使CMultilockCMultilock类对象类对象和和CSingleLockCSingleLock类对象的线程可用;否则,要访问资源类对象的线程可用;否则,要访问资源的线程必须等待。该参数的默认值为的线程必须等待。该参数的默认值为FALSEFALSE。 bManualReset bManualReset: 指定要创建的指定要创建的CEventCEvent对象是属于手工事件

48、对象还对象是属于手工事件对象还是自动事件对象。若为是自动事件对象。若为TRUETRUE,则为手工事件对象,否,则为手工事件对象,否则为自动事件对象。该参数默认值为则为自动事件对象。该参数默认值为FALSEFALSE。 补充说明:补充说明:在在MFCMFC中,中,CEventCEvent类对象有两种类型,类对象有两种类型,分别是所谓的手工事件和自动事件。对于自动事件,分别是所谓的手工事件和自动事件。对于自动事件,当其获得信号后,就会释放下一个可用的线程。一当其获得信号后,就会释放下一个可用的线程。一个自动个自动 CEventCEvent对象在被至少一个线程释放后会自动对象在被至少一个线程释放后会

49、自动返回到无信号状态;而人工事件对象获得信号后,返回到无信号状态;而人工事件对象获得信号后,释放所有可利用线程,直到调用成员函数释放所有可利用线程,直到调用成员函数ReSetEventReSetEvent()()将其设置为无信号状态时为止。将其设置为无信号状态时为止。 注意:注意:在创建在创建CEventCEvent类的对象时,默认创建的类的对象时,默认创建的是自动事件。是自动事件。 16.5.1 16.5.1 事件对象事件对象16.5.1 16.5.1 事件对象事件对象lpszNamelpszName: 指定要创建的事件对象的名字,如果该事件对象指定要创建的事件对象的名字,如果该事件对象将跨

50、进程使用,则此参数不能为将跨进程使用,则此参数不能为NULLNULL。如果该参数和。如果该参数和一个已经存在的一个已经存在的CEventCEvent对象相同,则该对象相同,则该构造函数构造函数返回返回一个对这个已存在对象的引用;如果参数和一个已存一个对这个已存在对象的引用;如果参数和一个已存在的非在的非CEventCEvent类的同步对象类的同步对象( (如如CMutex)CMutex)相同,则对象相同,则对象创建失败;创建失败;16.5.1 16.5.1 事件对象事件对象 lpsaAttribute lpsaAttribute: 指向指向SECURITY_ATTRIBUTESSECURITY

51、_ATTRIBUTES结构的指针,该参数结构的指针,该参数决定要创建的事件对象的安全属性,一般置为决定要创建的事件对象的安全属性,一般置为NULLNULL。16.5.1 16.5.1 事件对象事件对象16. BOOL SetEvent ( )16. BOOL SetEvent ( ): 将将CEventCEvent类对象的状态设置为有信号状态,并且释放类对象的状态设置为有信号状态,并且释放所有等待的线程;如果该事件是人工事件,则所有等待的线程;如果该事件是人工事件,则CEventCEvent类对类对象保持为有信号状态,直到调用成员函数象保持为有信号状态,直到调用成员函数ResetEvent()

52、ResetEvent()将将其重新设为无信号状态时为止,这样该事件就可以释放多其重新设为无信号状态时为止,这样该事件就可以释放多个线程;如果个线程;如果CEventCEvent类对象为自动事件,则在类对象为自动事件,则在SetEvent()SetEvent()将事件设置为有信号状态后,将事件设置为有信号状态后,CEventCEvent类对象由系统自动重类对象由系统自动重置为无信号状态,除非一个线程被释放。置为无信号状态,除非一个线程被释放。 如果该函数执行成功,则返回非零值,否则返回零。如果该函数执行成功,则返回非零值,否则返回零。16.5.1 16.5.1 事件对象事件对象3. BOOL R

53、esetEvent( ) 3. BOOL ResetEvent( ) 该函数将事件的状态设置为无信号状态,并保该函数将事件的状态设置为无信号状态,并保持该状态直至持该状态直至SetEvent()SetEvent()被调用时为止。由于自动被调用时为止。由于自动事件是由系统自动重置,故自动事件不需要调用该事件是由系统自动重置,故自动事件不需要调用该函数。函数。 如果该函数执行成功,返回非零值,否则返回如果该函数执行成功,返回非零值,否则返回非零。非零。16.5.1 16.5.1 事件对象事件对象4. BOOL PulseEvent( )4. BOOL PulseEvent( ) 发送一个事件脉冲,

54、该函数完成一系列操作后才返发送一个事件脉冲,该函数完成一系列操作后才返回。对于自动事件,回。对于自动事件,PulseEvent()PulseEvent()将事件设置为有信号将事件设置为有信号状态,等待一个线程被释放,将事件重置为无信号状态,状态,等待一个线程被释放,将事件重置为无信号状态,然后然后PulseEvent()PulseEvent()返回;对于人工事件,则将等待该事返回;对于人工事件,则将等待该事件的所有线程被释放,事件被自动重置为无信号状态,件的所有线程被释放,事件被自动重置为无信号状态,然后然后PulseEvent()PulseEvent()返回。返回。 一个一个CEventCE

55、vent对象在线程中被创建后,自动处于无信对象在线程中被创建后,自动处于无信号状态,但在另一个线程中可以调用号状态,但在另一个线程中可以调用Win32 API WaitForSingleObjectWin32 API WaitForSingleObject()()函数来监视其状态。函数来监视其状态。16.5.1 16.5.1 事件对象事件对象5. BOOL Unlock( )5. BOOL Unlock( )如果线程拥有事件对象,并且事件对象是一个如果线程拥有事件对象,并且事件对象是一个自动事件对象,则返回非零值,否则返回自动事件对象,则返回非零值,否则返回0。该函数用来释放事件对象,即把该函

56、数用来释放事件对象,即把Lock锁定的线锁定的线程解锁。程解锁。16.5.1 16.5.1 事件对象事件对象另外补充一个函数另外补充一个函数 BOOL Lock( )BOOL Lock( )该函数不是该函数不是CEvent类的成员函数,是类的成员函数,是CEvent的的父类父类CSyncObject类的成员函数,类的成员函数,CEvent通过继承可通过继承可以使用该函数。以使用该函数。该函数用来锁定事件对象,阻止线程的继续执该函数用来锁定事件对象,阻止线程的继续执行,直到事件对象处于活动状态为止。行,直到事件对象处于活动状态为止。6.WaitForSingleObject():其原型声明如下:

57、其原型声明如下:DWORDWaitForSingleObject(HANDLEhHandle,DWORDdwMilliseconds) hHandle hHandle为指向要监视的同步对象的句柄;为指向要监视的同步对象的句柄; dwMilliseconds dwMilliseconds为监视为监视hHandlehHandle所指向的对象所所指向的对象所设置的超时值,单位为毫秒。设置的超时值,单位为毫秒。16.5.1 16.5.1 事件对象事件对象 当在线程的执行函数中调用该函数时,线程暂时挂当在线程的执行函数中调用该函数时,线程暂时挂起,系统监视起,系统监视hHandlehHandle所指向的

58、对象的状态。如果经过所指向的对象的状态。如果经过dwMillisecondsdwMilliseconds毫秒后,毫秒后,hHandlehHandle指向的对象变为有信号指向的对象变为有信号状态,则状态,则WaitForSingleObject()WaitForSingleObject()返回,线程被释放,且返回,线程被释放,且返回值为返回值为WAIT_TIMEOUTWAIT_TIMEOUT;如果在挂起的;如果在挂起的dwMillisecondsdwMilliseconds毫秒内,线程所等待的对象在某一时刻变为有信号,则毫秒内,线程所等待的对象在某一时刻变为有信号,则该函数立即返回,返回值为该函

59、数立即返回,返回值为WAIT_OBJECT_0WAIT_OBJECT_0。16.5.1 16.5.1 事件对象事件对象 参数参数dwMillisecondsdwMilliseconds有两个具有特殊意义的值:有两个具有特殊意义的值:0 0和和INFINITEINFINITE。若为。若为0 0,则该函数立即返回;若为,则该函数立即返回;若为INFINITEINFINITE,则线程一直被挂起,直到,则线程一直被挂起,直到hHandlehHandle所指向所指向的对象变为有信号状态时为止。的对象变为有信号状态时为止。16.5.1 16.5.1 事件对象事件对象 CEvent:ResetEvent(

60、) CEvent:ResetEvent( )把对象设置为无信号状态,程把对象设置为无信号状态,程序在序在WaitForSingleObject(hHandle,INFINITE)WaitForSingleObject(hHandle,INFINITE)处等待。处等待。 CEvent:SetEvent( ) CEvent:SetEvent( )把对象设置为有信号状态,释放把对象设置为有信号状态,释放等待的线程。等待的线程。 如果如果CEventCEvent对象为自动事件,则当对象为自动事件,则当WaitForSingleObject(hHandle,INFINITE)WaitForSingleO

61、bject(hHandle,INFINITE)返回时,自动返回时,自动把把CEventCEvent对象重置为无信号状态。对象重置为无信号状态。16.5.1 16.5.1 事件对象事件对象总结以上,几个函数的使用顺序为:总结以上,几个函数的使用顺序为: B B线程在执行到线程在执行到CEventCEvent类成员函数类成员函数Lock( )Lock( )时将会时将会发生阻塞,而发生阻塞,而A A线程此时则可以在没有线程此时则可以在没有B B线程干扰的情线程干扰的情况下对共享资源进行处理,并在处理完成后通过成员况下对共享资源进行处理,并在处理完成后通过成员函数函数SetEvent( )SetEve

62、nt( )向向B B发出事件,使其被释放,得以对发出事件,使其被释放,得以对A A先前已处理完毕的共享资源进行操作。先前已处理完毕的共享资源进行操作。 16.5.1 16.5.1 事件对象事件对象另外通过一个例题来演示事件的工作原理:另外通过一个例题来演示事件的工作原理:16.5.2 16.5.2 临界区临界区 临界区(临界区(Critical SectionCritical Section)是一段代码)是一段代码, ,该代码该代码独占对某些共享资源的访问,在任意时刻只允许一个独占对某些共享资源的访问,在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时线程对共享资源进行访问。如

63、果有多个线程试图同时访问临界区,那么在有一个线程进入后其他所有试图访问临界区,那么在有一个线程进入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。界区的线程离开。 临界区在被释放后,其他线程可以继续抢占,并临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共享资源的目的。以此达到用原子方式操作共享资源的目的。16.5.2 16.5.2 临界区临界区 在使用临界区时,一般不允许其运行时间过长,在使用临界区时,一般不允许其运行时间过长,只要进入临界区的线程还没有离开,其他所有试图进只要进入临界区的线程还没有离开

64、,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响。程序的运行性能。尤其需要注会在一定程度上影响。程序的运行性能。尤其需要注意的是不要将等待用户输入或是其他一些外界干预的意的是不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。放,同样也会引起其他线程的长时间等待。16.5.2 16.5.2 临界区临界区 虽然临界区同步速度很快,但却虽然临界区同步速度很快,但却只能用来同步本进只能用来同步本进程内的线程

65、程内的线程,而不可用来同步多个进程中的线程。,而不可用来同步多个进程中的线程。 MFC MFC为临界区提供有一个为临界区提供有一个CCriticalSectionCCriticalSection类,使类,使用该类进行线程同步处理是非常简单的,只需在线程用该类进行线程同步处理是非常简单的,只需在线程函数中用函数中用CCriticalSectionCCriticalSection类成员函数类成员函数LockLock()和()和UnLockUnLock()标定出被保护的代码片段即可。()标定出被保护的代码片段即可。 16.5.2 16.5.2 临界区临界区 下面通过一段代码展示了临界区在保护多线程访

66、下面通过一段代码展示了临界区在保护多线程访问的共享资源中的作用。通过两个线程来分别对全局问的共享资源中的作用。通过两个线程来分别对全局变量变量g_cArray10g_cArray10进行写入操作,用临界区对象进行写入操作,用临界区对象g_clsCriticalSectiong_clsCriticalSection来保持线程的同步,并在开启来保持线程的同步,并在开启线程前对其进行初始化。为了使实验效果更加明显,线程前对其进行初始化。为了使实验效果更加明显,体现出临界区的作用,在线程函数对共享资源体现出临界区的作用,在线程函数对共享资源g_cArray10g_cArray10的写入时,以的写入时,

67、以Sleep( )Sleep( )函数延迟函数延迟1 1毫秒,毫秒,使其他线程同其抢占使其他线程同其抢占CPUCPU的可能性增大。如果不使用临的可能性增大。如果不使用临界区对其进行保护,则共享资源数据将被破坏。而使界区对其进行保护,则共享资源数据将被破坏。而使用临界区对线程保持同步后则可以得到正确的结果。用临界区对线程保持同步后则可以得到正确的结果。16.5.3 16.5.3 互斥互斥量量 互斥量(互斥量(MutexMutex)是一种用途非常广泛的内核)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访对象。能够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有

68、互斥对象的线程问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占有资源的线程在任务处理多个线程所访问。当前占有资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问共享资源。得后得以访问共享资源。图:互斥内核对象的工作模型图:互斥内核对象的工作模型16.5.3 16.5.3 互斥互斥量量 与其他几种内核对象不同,互斥对象在操作系与其他几种内

69、核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。为便于理解,可参照下图。的非常规操作。为便于理解,可参照下图。共享资源共享资源拥有互斥量拥有互斥量的线程的线程16.5.3 16.5.3 互斥互斥量量 互斥对象在互斥对象在MFCMFC中通过中通过CMutexCMutex类进行表述。使用类进行表述。使用CMutexCMutex类的方法非常简单,在构造类的方法非常简单,在构造CMutexCMutex类对象的同类对象的同时可以指明待查

70、询的互斥对象的名字,在构造函数返时可以指明待查询的互斥对象的名字,在构造函数返回后即可访问此互斥变量。回后即可访问此互斥变量。CMutexCMutex类也是只含有构造类也是只含有构造函数这唯一的成员函数,函数这唯一的成员函数,当完成对互斥对象保护资源当完成对互斥对象保护资源的访问后,可通过调用从父类的访问后,可通过调用从父类CSyncObjectCSyncObject继承的继承的UnLockUnLock()函数完成对互斥对象的释放。()函数完成对互斥对象的释放。16.5.3 16.5.3 互斥互斥量量CMutexCMutex类构造函数原型为:类构造函数原型为:其参数说明如下:其参数说明如下:C

71、Mutex(BOOLbInitiallyOwn=FALSE,LPCTSTRlpszName=NULL,LPSECURITY_ATTRIBUTESlpsaAttribute=NULL)16.5.3 16.5.3 互斥互斥量量 bInitiallyOwn bInitiallyOwn: 该参数指定是否创建了该参数指定是否创建了CMutexCMutex对象的线程最初拥对象的线程最初拥有由互斥量有由互斥量CMutexCMutex控制的共享资源的控制权。控制的共享资源的控制权。16.5.3 16.5.3 互斥互斥量量lpszNamelpszName: 指定要创建的指定要创建的CMutexCMutex对象的

72、名字。若该值为对象的名字。若该值为NULLNULL,则生成则生成CMutexCMutex对象没有命名。若与其它已经存在的对象没有命名。若与其它已经存在的CMutexCMutex对象重名,那么构造函数将创建一个参考给定对象重名,那么构造函数将创建一个参考给定名称的新名称的新CMutexCMutex对象。如果与其重名的不是对象。如果与其重名的不是CMutexCMutex对对象,那么构造过程失败。象,那么构造过程失败。16.5.3 16.5.3 互斥互斥量量 lpsaAttribute lpsaAttribute: 指向指向SECURITY_ATTRIBUTESSECURITY_ATTRIBUTES

73、结构的指针,该参数决结构的指针,该参数决定要创建的互斥量对象的安全属性,一般置为定要创建的互斥量对象的安全属性,一般置为NULLNULL。 在编写程序时,互斥对象多用在对那些为多个在编写程序时,互斥对象多用在对那些为多个线程所访问的内存块的保护上,可以确保任何线程线程所访问的内存块的保护上,可以确保任何线程在处理此内存块时都对其拥有可靠的独占访问权。在处理此内存块时都对其拥有可靠的独占访问权。16.5.3 16.5.3 互斥互斥量量 先通过书中的例子来了解互斥量先通过书中的例子来了解互斥量CMutexCMutex的工作的工作原理,然后通过另外一个例子来加深理解。原理,然后通过另外一个例子来加深

74、理解。 下面给出的例子即通过互斥量对象下面给出的例子即通过互斥量对象g_clsMutex对对共享内存块共享内存块g_cArrayg_cArray进行线程的独占访问保护。进行线程的独占访问保护。 16.5.4 16.5.4 信号信号灯灯 信号灯又称信号量(信号灯又称信号量(SemaphoreSemaphore),这一内核对),这一内核对象对线程的同步方式与前面几种方法不同,它允许象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在同一时刻访问此资源的最大线程数目。 在声明在声明C

75、SemaphoreCSemaphore对象时,可以指出允许的最大对象时,可以指出允许的最大资源计数和当前可用资源计数。资源计数和当前可用资源计数。 一般是将当前可用资源计数设置为最大资源计一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用数,每增加一个线程对共享资源的访问,当前可用资源计数就会减资源计数就会减1 1,只要当前可用资源计数是大于,只要当前可用资源计数是大于0 0的,就可以发出信号量信号。但是当前可用计数减的,就可以发出信号量信号。但是当前可用计数减小到小到0 0时则说明当前占用资源的线程数已经达到了所时则说明当前占用资源的线程数已经达到了所允许的

76、最大数目,不能在允许其他线程的进入,此允许的最大数目,不能在允许其他线程的进入,此时的信号量信号将无法发出。时的信号量信号将无法发出。16.5.4 16.5.4 信号信号灯灯16.5.4 16.5.4 信号信号灯灯 线程在处理完共享资源后,应在离开的同时通线程在处理完共享资源后,应在离开的同时通过过Unlock( )Unlock( )函数将当前可用资源计数加函数将当前可用资源计数加1 1。否则将。否则将会出现当前正在处理共享资源的实际线程数并没有会出现当前正在处理共享资源的实际线程数并没有达到要限制的数值,而其他线程却因为当前可用资达到要限制的数值,而其他线程却因为当前可用资源计数为源计数为0

77、 0而仍无法进入的情况。而仍无法进入的情况。 在任何时候当前可用资源计数决不可能大于最在任何时候当前可用资源计数决不可能大于最大资源计数。大资源计数。16.5.4 16.5.4 信号信号灯灯图:信号量对象对资源的控制图:信号量对象对资源的控制16.5.4 16.5.4 信号信号灯灯 信号量的使用特点使其更适用于对信号量的使用特点使其更适用于对SocketSocket(套接字)程序中线程的同步。(套接字)程序中线程的同步。 下面给出一个例子。下面给出一个例子。 在例子中,开启线程前首先创建了一个初始计数在例子中,开启线程前首先创建了一个初始计数和最大资源计数均为和最大资源计数均为2 2的信号量对

78、象的信号量对象g_Semaphoreg_Semaphore。即。即在同一时刻只允许在同一时刻只允许2 2个线程进入由个线程进入由g_Semaphoreg_Semaphore保护的保护的共享资源。共享资源。16.5.4 16.5.4 信号信号灯灯 随后开启的三个线程均试图访问此共享资源,在随后开启的三个线程均试图访问此共享资源,在前两个线程试图访问共享资源时,由于前两个线程试图访问共享资源时,由于g_Semaphoreg_Semaphore的的当前可用资源计数分别为当前可用资源计数分别为2 2和和1 1,此时的,此时的g_Semaphoreg_Semaphore是是可以得到通知的,也就是说位于线

79、程入口处的可以得到通知的,也就是说位于线程入口处的WaitForSingleObjectWaitForSingleObject()()将立即返回,而在前两个线程进入到保护区域后,将立即返回,而在前两个线程进入到保护区域后,g_Semaphoreg_Semaphore的当前资源计数减少到的当前资源计数减少到0 0,g_Semaphoreg_Semaphore将将不再得到通知,不再得到通知,WaitForSingleObject()WaitForSingleObject()将线程挂起。将线程挂起。直到此前进入到保护区的线程退出后才能得以进入。直到此前进入到保护区的线程退出后才能得以进入。 16.5.4 16.5.4 信号信号灯灯16.6 本章小结l线程与进程的基本概念l与线程相关的一些概念和函数l用于线程同步的对象以及MFC中的四个较为常用的线程同步类

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 建筑/环境 > 施工组织

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号