1、WindowsWindows网络编程技术网络编程技术第第2 2章单机资源共享的应用章单机资源共享的应用编程编程授课老师:胡鸣授课老师:胡鸣数学与计算机学院数学与计算机学院计算机系计算机系教学目的教学目的 理解进程,线程和动态连接库的概念;理解进程,线程和动态连接库的概念;掌握进程的创建方法,进程间的通信;掌握进程的创建方法,进程间的通信;掌握线程创建的方法,多线程间的通信;掌握线程创建的方法,多线程间的通信;掌握多线程的同步机制;掌握多线程的同步机制;掌握动态链接库的编写和使用;掌握动态链接库的编写和使用;本章提纲本章提纲2.1 2.1 进程间通信进程间通信2.1.12.1.1进程间通信应用实
2、例及概念进程间通信应用实例及概念2.1.22.1.2进程的创建与终止进程的创建与终止2.1.32.1.3内存文件映射内存文件映射2.2 2.2 多线程通信多线程通信2.2.12.2.1多线程应用实例及概念多线程应用实例及概念2.2.22.2.2线程的创建、挂起、激活和终止线程的创建、挂起、激活和终止2.2.32.2.3线程的优先级线程的优先级2.3 2.3 同步控制机制同步控制机制2.3.12.3.1同步控制应用实例及意义同步控制应用实例及意义2.3.22.3.2同步控制类型及应用条件同步控制类型及应用条件2.3.32.3.3应用实例的算法与实现应用实例的算法与实现2.4 2.4 动态链接库动
3、态链接库2.4.12.4.1静态链接库与动态链接库的应用实例静态链接库与动态链接库的应用实例2.4.22.4.2动态链接库的创建和调用方法动态链接库的创建和调用方法2.4.32.4.3动态链接库应用的条件动态链接库应用的条件2.1.12.1.1进程间通信应用实例及概念进程间通信应用实例及概念 程序和进程程序和进程 应用程序和进程在概念上是有一定区别的,前者是静应用程序和进程在概念上是有一定区别的,前者是静态的程序代码,而后者是动态的实体。只有应用程序加态的程序代码,而后者是动态的实体。只有应用程序加载到系统中后才能成为一个进程。载到系统中后才能成为一个进程。独立进程和共享进程独立进程和共享进程
4、 独立运行的程序称为独立进程独立运行的程序称为独立进程 ;另外,应用程序可能;另外,应用程序可能启动多个进程,一个进程空间可以运行多个程序,这就启动多个进程,一个进程空间可以运行多个程序,这就是共享进程。是共享进程。我们以第二章代码我们以第二章代码“命名管道命名管道”为例来了解进程间通为例来了解进程间通信的基本情况。进程信的基本情况。进程A A通过命名管道(后面会介绍)的通过命名管道(后面会介绍)的方式传递两个参数给进程方式传递两个参数给进程B B,进程,进程B B将这两个参数进行将这两个参数进行计算后,把结果通过命名管道的方式返回给进程计算后,把结果通过命名管道的方式返回给进程A A。具体的
5、过程结合下面的图来描述:具体的过程结合下面的图来描述:进程间通信实例进程间通信实例 进程进程A A先启动,然后创建进程先启动,然后创建进程B B,;,;进程进程B B创建成功后先创建命名管道,然后创建创建成功后先创建命名管道,然后创建并连接一个命名通道。并连接一个命名通道。进程进程A A打开进程打开进程B B建立的命名管道,同时向命名建立的命名管道,同时向命名管道写数据,进程管道写数据,进程B B获取到进程获取到进程A A传过来的数据,传过来的数据,进行处理并将结果传回给进程进行处理并将结果传回给进程A A2.1.22.1.2进程的创建与终止进程的创建与终止 在上面的例子中,进程在上面的例子中
6、,进程A A有一个操作是创建进程有一个操作是创建进程B B。创建进程的。创建进程的函数是:函数是:CreateProcess CreateProcess BOOL CreateProcess(BOOL CreateProcess(LPCTSTR lpApplicationName,/LPCTSTR lpApplicationName,/执行程序文件名执行程序文件名LPTSTR lpCommandLine,/LPTSTR lpCommandLine,/参数行参数行LPSECURITY_ATTRIBUTES lpProcessAttributes,/LPSECURITY_ATTRIBUTES lp
7、ProcessAttributes,/进进程安全参数程安全参数 LPSECURITY_ATTRIBUTES lpThreadAttributes,/LPSECURITY_ATTRIBUTES lpThreadAttributes,/线线程安全参数程安全参数BOOL bInheritHandles,/BOOL bInheritHandles,/继承标记继承标记DWORD dwCreationFlags,/DWORD dwCreationFlags,/创建标记创建标记LPVOID lpEnvironment,/LPVOID lpEnvironment,/环境变量环境变量LPCTSTR lpCurr
8、entDirectory,/LPCTSTR lpCurrentDirectory,/运行该子进程的初始目录运行该子进程的初始目录LPSTARTUPINFO lpStartupInfo,/LPSTARTUPINFO lpStartupInfo,/创建该子进程的相关参创建该子进程的相关参数数LPPROCESS_INFORMATION lpProcessInformation /LPPROCESS_INFORMATION lpProcessInformation /创建后用于被创建子进程的信息创建后用于被创建子进程的信息););进程创建与正常结束进程创建与正常结束 进程是应用程序的执行实例,进程创建
9、成功后,进程是应用程序的执行实例,进程创建成功后,操作系统会给该进程分配私有的虚拟地址空间、操作系统会给该进程分配私有的虚拟地址空间、代码、数据和其他系统资源;进程结束后,操代码、数据和其他系统资源;进程结束后,操作系统会回收该进程所占用的系统资源。一般作系统会回收该进程所占用的系统资源。一般来讲(不使用特殊技术)进程是无法突破进程来讲(不使用特殊技术)进程是无法突破进程边界存取其他进程内的存储空间里的数据,这边界存取其他进程内的存储空间里的数据,这也是我们要学习进程通信的原因。也是我们要学习进程通信的原因。当主线程的进入点函数返回时,进程也就随之当主线程的进入点函数返回时,进程也就随之结束。
10、这种进程的终止方式是进程的正常退出,结束。这种进程的终止方式是进程的正常退出,进程中的所有线程资源都能够得到正确的清除。进程中的所有线程资源都能够得到正确的清除。除了这种进程的正常退出方式外,有时还需要除了这种进程的正常退出方式外,有时还需要在程序中通过代码来强制结束本进程或其他进在程序中通过代码来强制结束本进程或其他进程的运行。程的运行。进程强制结束进程强制结束 1.1.使用使用ExitProcess()ExitProcess()结束进程结束进程 ExitProcess()ExitProcess()函数的原型为:函数的原型为:voidExitProcess(UINTuExitCode);vo
11、idExitProcess(UINTuExitCode);2.2.使用使用TerminateProcess()TerminateProcess()结束进程结束进程ExitProcess()ExitProcess()只能强制执行本进程的退出,如果只能强制执行本进程的退出,如果要在一个进程中强制结束其他进程就要用要在一个进程中强制结束其他进程就要用TerminateProcess()TerminateProcess()来实现。来实现。BOOLTerminateProcess(HANDLEhProcess,BOOLTerminateProcess(HANDLEhProcess,UINTuExitCo
12、de);UINTuExitCode);应该尽可能的让进程正常退出;应该尽可能的让进程正常退出;在强制终止进程的时候应该考虑资源释放的问题;在强制终止进程的时候应该考虑资源释放的问题;2.1.32.1.3内存文件映射内存文件映射 前面讲到过,每个进程有自己的地址空间,一个前面讲到过,每个进程有自己的地址空间,一个进程不能轻易地访问另一个进程地址空间中的数进程不能轻易地访问另一个进程地址空间中的数据,必须采用特殊的方式来进行进程间的通信。据,必须采用特殊的方式来进行进程间的通信。本节以第二章代码本节以第二章代码“内存文件映射内存文件映射”为例介绍利为例介绍利用内存文件映射实现进程间通信。用内存文件
13、映射实现进程间通信。1 1、利用内存映射文件进行文件、利用内存映射文件进行文件I/OI/O操作,进行操作,进行文件文件I/OI/O操作需要下面几个步骤操作需要下面几个步骤:步骤一:调用步骤一:调用CreateFile()CreateFile()函数,以适当的函数,以适当的方式创建或打开一个文件核心对象;如果是内存方式创建或打开一个文件核心对象;如果是内存文件,这一步忽略;文件,这一步忽略;步骤二:把步骤二:把CreateFile()CreateFile()函数返回的文件句函数返回的文件句柄作为参数,传给柄作为参数,传给CreateFileMapping()CreateFileMapping()
14、函函数,由数,由CreateFileMapping()CreateFileMapping()函数创建一个函数创建一个文件映射核心对象的适当属性;如果是内存文文件映射核心对象的适当属性;如果是内存文件,则句柄是:件,则句柄是:0 xFFFFFFFF0 xFFFFFFFF;步骤三:创建了文件映射核心对象后,调用步骤三:创建了文件映射核心对象后,调用MapViewOfFile()MapViewOfFile()函数,告诉系统把文件的函数,告诉系统把文件的哪一部分映射到进程的地址空间中,以何种方哪一部分映射到进程的地址空间中,以何种方式映射;式映射;步骤四:利用步骤四:利用MapViewOfFile()
15、MapViewOfFile()函数返回函数返回的指针来使用文件数据;的指针来使用文件数据;步骤五:操作完毕后,调用步骤五:操作完毕后,调用UnmapViewOfFile()UnmapViewOfFile()函数,告诉系统撤销对函数,告诉系统撤销对文件映射核心对象的映射;文件映射核心对象的映射;步骤六:使用步骤六:使用CloseHandle()CloseHandle()函数关闭文函数关闭文件映射核心对象;件映射核心对象;步骤七:使用步骤七:使用CloseHandle()CloseHandle()函数关闭文函数关闭文件核心对象;件核心对象;2.2.12.2.1多线程应用实例及概念多线程应用实例及概
16、念 多线程是这样一种机制,它允许在程序中并发执多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼行多个指令流,每个指令流都称为一个线程,彼此间互相独立。线程又称为轻量级进程,它和进此间互相独立。线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。得线程间的通信远较进程简单。多线程和传统的单线程在程序设计上最大
17、的区别多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题。程调度,同步等问题。存钱和取钱的例子,我们利用第二章代码存钱和取钱的例子,我们利用第二章代码“三线三线程同步程同步”来说明。来说明。例子例子造成这种结果的原因是由于各个线造成这种结果的原因是由于各个线程的控制流彼此独立,使得各个程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,线程之间的代码是乱序执行的,同时他们可能会同时访问公共变同时他们可能会同时访问公
18、共变量,导致数据的不一致。如图量,导致数据的不一致。如图2.32.3中,第一行和第二行的结果中,第一行和第二行的结果正常,但是第三行和第四行是线正常,但是第三行和第四行是线程同时对程同时对nResValuenResValue进行操作,进行操作,进程进程2 2在在0 0的基础上减的基础上减1010,而进,而进程程1 1则在则在0 0的基础上加的基础上加1010,从而,从而导致导致Result2=-10Result2=-10,Result1=10Result1=10。这是个错误的结。这是个错误的结果。也就是说,在同一时刻,只果。也就是说,在同一时刻,只能有一个线程对能有一个线程对nResValue
19、nResValue操操作才是正确的,也只有这样才不作才是正确的,也只有这样才不会出现上面的情况。后面会专门会出现上面的情况。后面会专门讲这个问题讲这个问题同步控制。同步控制。2.2.22.2.2线程的创建、挂起、激活和终止线程的创建、挂起、激活和终止 一个进程的主线程是由操作系统自动生成的,如果让一一个进程的主线程是由操作系统自动生成的,如果让一个主线程创建额外的子线程,可以通过个主线程创建额外的子线程,可以通过CreateThreadCreateThread函数来完成。若线程创建成功,返回函数来完成。若线程创建成功,返回值为新线程的句柄;失败则返回值为新线程的句柄;失败则返回NULLNULL
20、。在创建的线程的时候,可以指定线程的初始状态。线程在创建的线程的时候,可以指定线程的初始状态。线程在创建的时候,有两种状态可以指定,执行状态和挂起在创建的时候,有两种状态可以指定,执行状态和挂起状态。通过设置状态。通过设置CreateThreadCreateThread的第五个参数可以设的第五个参数可以设置线程的初始状态,当参数为时,线程就会立即执行,置线程的初始状态,当参数为时,线程就会立即执行,而参数设置为而参数设置为CREATE_SUSPENDEDCREATE_SUSPENDED时,线程创建时,线程创建后并不马上执行,而是被挂起。要想该线程能够执行,后并不马上执行,而是被挂起。要想该线程
21、能够执行,必须在主线程或者其他的线程里调用必须在主线程或者其他的线程里调用ResumeThreadResumeThread函数并传递给它调用函数并传递给它调用CreateThreadCreateThread时返回的线程句柄来激活该进程。时返回的线程句柄来激活该进程。线程的挂起与激活线程的挂起与激活 除了在创建线程的时候可以挂起线程,我们还可以通过除了在创建线程的时候可以挂起线程,我们还可以通过函数函数SuspendThreadSuspendThread来挂起线程,一个线程可以被来挂起线程,一个线程可以被挂起多次。线程可以自行暂停运行,但是不能自行恢复挂起多次。线程可以自行暂停运行,但是不能自行
22、恢复运行。如果一个线程被挂起运行。如果一个线程被挂起n n次,则该线程也必须被恢次,则该线程也必须被恢复复n n次才可能得以执行,该次才可能得以执行,该SuspendThreadSuspendThread的声明的声明如下如下 :DWORD SuspendThread(HANDLE hThread);DWORD SuspendThread(HANDLE hThread);其中其中hThreadhThread是线程句柄是线程句柄 激活一个线程时,系统会先检查线程挂起的次数,如果激活一个线程时,系统会先检查线程挂起的次数,如果挂起的次数为,则表示该线程并非处于挂起状态,否挂起的次数为,则表示该线程并
23、非处于挂起状态,否则,该线程的挂起次数值将被减。则,该线程的挂起次数值将被减。ResumeThreadResumeThread函数很简单:函数很简单:DWORD ResumeThread(HANDLE hThread)DWORD ResumeThread(HANDLE hThread),其中其中hThreadhThread是线程句柄。是线程句柄。线程的终止与休眠线程的终止与休眠 当要中止一个线程的时候,类似于进程的中止。当要中止一个线程的时候,类似于进程的中止。线程的终止有如下四种方式:线程的终止有如下四种方式:(1 1)线程函数返回;)线程函数返回;(2 2)线程自身调用)线程自身调用Exi
24、tThread ExitThread 函数即终止自函数即终止自己己 ;(3 3)同一进程或其他进程的线程调用)同一进程或其他进程的线程调用TerminateThreadTerminateThread函数函数 ;(4 4)包含线程的进程终止。)包含线程的进程终止。VOID Sleep(DWORD dwMilliseconds);VOID Sleep(DWORD dwMilliseconds);该函数可使线程暂停自己的运行,直到该函数可使线程暂停自己的运行,直到dwMillisecondsdwMilliseconds毫秒过去为止。它告诉系统,毫秒过去为止。它告诉系统,自身不想在某个时间段内被调度。
25、自身不想在某个时间段内被调度。2.2.32.2.3线程的优先级线程的优先级 线程是有一定的优先级,操作系统会根据线程的优先级线程是有一定的优先级,操作系统会根据线程的优先级进行调度,分配进行调度,分配CPUCPU时间片,一个线程的优先级首先时间片,一个线程的优先级首先属于一个类,其实就是一个进程,然后是在该类中的相属于一个类,其实就是一个进程,然后是在该类中的相对位置。线程的优先级的计算如下对位置。线程的优先级的计算如下:线程优先级线程优先级 =进程类基本优先级进程类基本优先级 +线程相对优先级线程相对优先级 进程类的基本优先级包括:进程类的基本优先级包括:(1 1)实时:)实时:REALTI
26、ME_PRIORITY_CLASSREALTIME_PRIORITY_CLASS;(2 2)高:)高:HIGH _PRIORITY_CLASSHIGH _PRIORITY_CLASS;(3 3)高于正常:)高于正常:ABOVE_NORMAL_PRIORITY_CLASSABOVE_NORMAL_PRIORITY_CLASS;(4 4)正常:)正常:NORMAL _PRIORITY_CLASSNORMAL _PRIORITY_CLASS;(5 5)低于正常:)低于正常:BELOW_ NORMAL BELOW_ NORMAL _PRIORITY_CLASS_PRIORITY_CLASS;(6 6)
27、空闲:)空闲:IDLE_PRIORITY_CLASSIDLE_PRIORITY_CLASS。实例图实例图线程的相对优先级线程的相对优先级(1 1)空闲:)空闲:THREAD_PRIORITY_IDLETHREAD_PRIORITY_IDLE;(2 2)最低线程:)最低线程:THREAD_PRIORITY_LOWESTTHREAD_PRIORITY_LOWEST;(3 3)低于正常线程:)低于正常线程:THREAD_PRIORITY_BELOW_NORMALTHREAD_PRIORITY_BELOW_NORMAL;(4 4)正常线程:)正常线程:THREAD_PRIORITY_ NORMAL T
28、HREAD_PRIORITY_ NORMAL(缺省缺省);(5 5)高于正常线程:)高于正常线程:THREAD_PRIORITY_ABOVE_NORMALTHREAD_PRIORITY_ABOVE_NORMAL;(6 6)最高线程:)最高线程:THREAD_PRIORITY_HIGHESTTHREAD_PRIORITY_HIGHEST;(7 7)关键时间:)关键时间:THREAD_PRIOTITY_CRITICALTHREAD_PRIOTITY_CRITICAL。控制线程的优先级控制线程的优先级 通常,应用程序可以使用下列方法控制线程的通常,应用程序可以使用下列方法控制线程的相对优先级:相对优
29、先级:当使用当使用CreateProcessCreateProcess时,可以指定进程的时,可以指定进程的优先级;若未指定,缺省优先级为正常;优先级;若未指定,缺省优先级为正常;可以使用可以使用SetPriotityClassSetPriotityClass来改变进程的优来改变进程的优先级。这将影响到进程内的所有线程的优先级,先级。这将影响到进程内的所有线程的优先级,但是线程的相对优先级不会变;但是线程的相对优先级不会变;可以通过可以通过SetThreadPrioritySetThreadPriority来任何一个线来任何一个线程的优先级;程的优先级;当一个线程首次被创建时,它的优先级类等同当
30、一个线程首次被创建时,它的优先级类等同于它所属于的进程优先级类。于它所属于的进程优先级类。2.3.12.3.1同步控制应用实例及意义同步控制应用实例及意义 进程中的所有线程共享进程的虚拟地址空间,这意味进程中的所有线程共享进程的虚拟地址空间,这意味着所有线程都可以访问进程中的资源,一方面,这种着所有线程都可以访问进程中的资源,一方面,这种机制为我们的编程带来方便,但同时也会带来访问冲机制为我们的编程带来方便,但同时也会带来访问冲突,其结果会导致数据的错乱。线程的同步是为了协突,其结果会导致数据的错乱。线程的同步是为了协调多个线程的执行,保证数据完整性调多个线程的执行,保证数据完整性,以第二章代
31、码以第二章代码“三线程同步三线程同步”为例。为例。多线程编程一个最具挑战性的问题就是:如何让一个多线程编程一个最具挑战性的问题就是:如何让一个线程与另一个线程合作。线程与另一个线程合作。当程序当程序1 1调用程序调用程序2 2时,程序时,程序1 1停下不动,直到程序停下不动,直到程序2 2完成回到程序完成回到程序1 1来,程序来,程序1 1才继续下去,这就是所谓的才继续下去,这就是所谓的同步。如果程序同步。如果程序1 1调用程序调用程序2 2后,径自继续自己的下一后,径自继续自己的下一个动作,那么两者之间就是异步。个动作,那么两者之间就是异步。Win32APIWin32API中的中的SendM
32、assage()SendMassage()就是同步行为,而就是同步行为,而PostMassage()PostMassage()就是异步行为,如图所示就是异步行为,如图所示同步与异步同步与异步两个例子的比较两个例子的比较2.3.22.3.2同步控制类型及应用条件同步控制类型及应用条件 线程的同步是通过同步对象来实现的。同步对象线程的同步是通过同步对象来实现的。同步对象是一个数据结构,用来协调多线程的执行,它可是一个数据结构,用来协调多线程的执行,它可以被多个线程共享。以被多个线程共享。同步对象主要有五种:临界区域(同步对象主要有五种:临界区域(Critical Critical SectionS
33、ection)、互斥信号()、互斥信号(MutexMutex)、信号量)、信号量(SemaphoneSemaphone)、事件对象)、事件对象(Event)(Event)和互锁和互锁变量变量(Interlocked)(Interlocked)。两个重要的函数:监测单个同步对象的两个重要的函数:监测单个同步对象的WaitForSingleObject()WaitForSingleObject()函数和可以同时监测函数和可以同时监测多个同步对函数多个同步对函数WaitForMultipleObjects()WaitForMultipleObjects()象象两个重要的函数两个重要的函数 DWORD
34、 WaitForSingleObject(HANDLE hHandle,DWORD WaitForSingleObject(HANDLE hHandle,DWORD dwMilliseconds);DWORD dwMilliseconds);参数参数hHandlehHandle是同步对象的句柄。是同步对象的句柄。参数参数dwMillisecondsdwMilliseconds是以毫秒为单位的超时间隔,如果该参数是以毫秒为单位的超时间隔,如果该参数为为0 0,那么函数就测试同步对象的状态并立即返回,如果该参,那么函数就测试同步对象的状态并立即返回,如果该参数为数为INFINITEINFINITE
35、,则超时间隔是无限的。,则超时间隔是无限的。DWORD WaitForMultipleObjects(DWORD nCount,DWORD WaitForMultipleObjects(DWORD nCount,CONST HANDLE CONST HANDLE*lpHandles,BOOL bWaitAll,DWORD lpHandles,BOOL bWaitAll,DWORD dwMilliseconds);dwMilliseconds);参数参数nCountnCount是句柄数组中句柄的数目。是句柄数组中句柄的数目。参数参数lpHandleslpHandles代表一个句柄数组。代表一个句
36、柄数组。参数参数bWaitAllbWaitAll说明了等待类型,如果为说明了等待类型,如果为TRUETRUE,那么函数在所有,那么函数在所有对象都有信号后才返回,如果为对象都有信号后才返回,如果为FALSEFALSE,则只要有一个对象变,则只要有一个对象变成有信号的,函数就返回。成有信号的,函数就返回。参数参数dwMillisecondsdwMilliseconds是以毫秒为单位的超时间隔,如果该参数是以毫秒为单位的超时间隔,如果该参数为为0 0,那么函数就测试同步对象的状态并立即返回,如果该参,那么函数就测试同步对象的状态并立即返回,如果该参数为数为INFINITEINFINITE,则超时间
37、隔是无限的。,则超时间隔是无限的。临界区域临界区域(Critical Sections)(Critical Sections)临界区是保证在某一个时间只有一个线程可以访临界区是保证在某一个时间只有一个线程可以访问数据的方法。使用它的过程中,需要给各个线问数据的方法。使用它的过程中,需要给各个线程提供一个共享的临界区对象,无论哪个线程占程提供一个共享的临界区对象,无论哪个线程占有临界区对象,都可以访问受到保护的数据,这有临界区对象,都可以访问受到保护的数据,这时候其他的线程要等待,直到该线程释放临界区时候其他的线程要等待,直到该线程释放临界区对象为止,临界区被释放后,另外的线程可以强对象为止,临
38、界区被释放后,另外的线程可以强占这个临界区,以便访问共享的数据。占这个临界区,以便访问共享的数据。所谓临界区域就是指一块所谓临界区域就是指一块“用来处理一份被共享用来处理一份被共享资源资源”的程序代码。用户模式下资源每一次只能的程序代码。用户模式下资源每一次只能一个进程使用一个进程使用,以第二章代码,以第二章代码“临界区临界区”为例。为例。临界区域相关函数临界区域相关函数 对类型为对类型为CRITICAL_SECTIONCRITICAL_SECTION的局部变量初始化,的局部变量初始化,方法是调用函数方法是调用函数Void Void InitializeCriticalSectionIniti
39、alizeCriticalSection(LPCRITICAL_SECTION(LPCRITICAL_SECTION lpCriticalSectionlpCriticalSection ););当使用完临界区时,清除它使用函数当使用完临界区时,清除它使用函数void void DeleteCriticalSectionDeleteCriticalSection(LPCRITICAL_SECTION(LPCRITICAL_SECTION lpCriticalSectionlpCriticalSection ););进入临界区时,使用函数进入临界区时,使用函数void void EnterCri
40、ticalSectionEnterCriticalSection(LPCRITICAL_SECTION(LPCRITICAL_SECTION lpCriticalSectionlpCriticalSection ););离开临界区时,使用函数离开临界区时,使用函数void void LeaveCriticalSectionLeaveCriticalSection(LPCRITICAL_SECTION(LPCRITICAL_SECTION lpCriticalSectionlpCriticalSection ););互斥信号(互斥信号(MutexMutex)互斥(互斥(MutexMutex)是一
41、种用途非常广泛的内核对象。能)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。同临界够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资区有些类似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。与对象交出,以便其他线程在获得后得
42、以访问资源。与其他几种内核对象不同,互斥对象在操作系统中拥有其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些其他内核对象所不能进行的非常规操作。许其进行一些其他内核对象所不能进行的非常规操作。牺牲速度增加弹性,它是在内核中锁住变量(开销牺牲速度增加弹性,它是在内核中锁住变量(开销大),可以跨进程使用,等待大),可以跨进程使用,等待mutexmutex可以指定等待时可以指定等待时间,以第二章代码间,以第二章代码“互斥信号互斥信号”为例。为例。互斥信号相关函数互斥信号相关函数 产生产生MutexM
43、utex,调用函数,调用函数HANDLE HANDLE CreateMutex(LPSECURITY_ATTRIBUTES CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributeslpMutexAttributes,BOOL,BOOL bInitialOwnerbInitialOwner,LPCTSTR LPCTSTR lpNamelpName ););关闭关闭MutexMutex,使用函数,使用函数BOOL BOOL CloseHandle(HANDLE CloseHandle(HANDLE hObjecthObject););打开一个互斥信号,
44、调用函数打开一个互斥信号,调用函数HANDLE HANDLE OpenMutex(DWORD OpenMutex(DWORD dwDesiredAccessdwDesiredAccess,BOOL BOOL bInheritHandlebInheritHandle,LPCTSTR,LPCTSTR lpNamelpName););获得获得mutexmutex所有权可以使用所有权可以使用Win32Win32的的WaitWait函数。函数。释放释放mutexmutex,调用函数,调用函数BOOL BOOL ReleaseMutex(HANDLE ReleaseMutex(HANDLE hMutexh
45、Mutex ););信号量(信号量(SemaphoreSemaphore)信号量(信号量(SemaphoreSemaphore)内核对象对线程的同步方式)内核对象对线程的同步方式与前面几种方法不同,它允许多个线程在同一时刻访与前面几种方法不同,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目,以第二章代码最大线程数目,以第二章代码“信号量信号量”为例。为例。信号量锁住同类一定数量的资源信号量锁住同类一定数量的资源 信号量的使用特点使其更适用于对信号量的使用特点使其更适用于对SocketSocket(套接字)(套接
46、字)程序中线程的同步。例如,网络上的程序中线程的同步。例如,网络上的HTTPHTTP服务器要对服务器要对同一时间内访问同一页面的用户数加以限制,这时可同一时间内访问同一页面的用户数加以限制,这时可以为没一个用户对服务器的页面请求设置一个线程,以为没一个用户对服务器的页面请求设置一个线程,而页面则是待保护的共享资源,通过使用信号量对线而页面则是待保护的共享资源,通过使用信号量对线程的同步作用可以确保在任一时刻无论有多少用户对程的同步作用可以确保在任一时刻无论有多少用户对某一页面进行访问,只有不大于设定的最大用户数目某一页面进行访问,只有不大于设定的最大用户数目的线程能够进行访问,而其他的访问企图
47、则被挂起,的线程能够进行访问,而其他的访问企图则被挂起,只有在有用户退出对此页面的访问后才有可能进入。只有在有用户退出对此页面的访问后才有可能进入。信号量信号量相关函数相关函数 产生信号量,调用函数产生信号量,调用函数HANDLE HANDLE CreateSemaphoreCreateSemaphore(LPSECURITY_ATTRIBUTES(LPSECURITY_ATTRIBUTES lpSemaphoreAttributeslpSemaphoreAttributes,LONG,LONG lInitialCountlInitialCount,LONG,LONG lMaximumCoun
48、tlMaximumCount,LPCTSTR LPCTSTR lpNamelpName ););获得信号量,可以使用获得信号量,可以使用Win32Win32的的WaitWait函数。函数。释放信号量,调用函数释放信号量,调用函数BOOL BOOL ReleaseSemaphore(HANDLE ReleaseSemaphore(HANDLE hSemaphorehSemaphore,LONG LONG lReleaseCountlReleaseCount,LPLONG,LPLONG lpPreviousCountlpPreviousCount ););关闭信号量,可以使用关闭信号量,可以使用C
49、losehandle()Closehandle()函数函数事件对象(事件对象(Event ObjectEvent Object)事件事件(Event)(Event)为一最具有弹性的核心对象,是为一最具有弹性的核心对象,是WIN32WIN32提供的最提供的最灵活的线程间同步方式,事件可以处于激发状态灵活的线程间同步方式,事件可以处于激发状态(signaled or(signaled or true)true)或未激发状态或未激发状态(unsignal or false)(unsignal or false)),这两种状态全),这两种状态全由程序控制,不会成为由程序控制,不会成为WaitWait函
50、数的副作用。它是用来通知其函数的副作用。它是用来通知其他进程他进程/线程某个操作已经完成。根据状态变迁方式的不同,事线程某个操作已经完成。根据状态变迁方式的不同,事件可分为两类:件可分为两类:(1 1)手动设置:这种对象只可能用程序手动设置,在需要该)手动设置:这种对象只可能用程序手动设置,在需要该事件或者事件发生时,采用事件或者事件发生时,采用SetEventSetEvent及及ResetEventResetEvent来进行来进行设置。当有多个线程都在等待一个线程运行结束,我们就可以设置。当有多个线程都在等待一个线程运行结束,我们就可以使用人工重置事件,在被等待的线程结束时设置该事件为有信使