资源描述
第二章 windows的进程管理2.1 实验一:线程的创建与撤销2.1.1 实验目的(1)熟悉windows系统提供的线程创建与撤销系统调用.(2)掌握windows系统环境下线程的创建与撤销方法.2.1.2 实验准备知识1.线程的创建CeateThread()完成线程的创建.它在调用进程的地址空间上创建一个线程,执行指定的函数,并返回新建立的线程的句柄.原型:HANDLE CeateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPSECURITY_START_ROUTINE lpStartAddress,LPVOID lpparameter,DWORD dwCreationFlags,LPDWORD lpThreadId);参数说明:(1)lpThreadAttributes:为线程指定安全属性.为NULL时,线程得到一个默认的安全描述符.(2)dwStackSize:线程堆栈的大小.其值为0时,其大小与调用该线程的线程堆栈大小相同.(3)lpStartAddress:指定线程要执行的函数.(4)lpparameter:函数中要传递的参数.(5)dwCreationFlags:指定线程创建后所处的状态.若为CRRATE_SUSPENDED,表示创建后出于挂起状态,用ResumeThread()激活线程才可以执行.若该值为0,表示线程创建后立即执行.(6)lpThreadId:用一个32位的变量接受系统返回的线程标识符.若该值设为NULL,系统不返回线程标识符.返回值:如果线程创建成功,将返回线程的句柄;如果失败,系统返回NULL,可以调用函数GetLastError查询失败的原因.用法举例:static HANDLE hHandle1=NULL; /用于存储线程返回句柄的变量DWORD dwThreadID1; /用于存储线程标识符的变量/创建一个名为ThreadName1的线程hHandle1=CeateThread(LPSECURITY_ATTRIBUTES) NULL 0, (LPSECURITY_START_ROUTINE)ThreadName1, (LPDWORD)NULL, 0,&dwThreadID1);2.撤销进程ExitThread()用于撤销当前进程.原型:VOID ExitThread(DWORD dwExitCode); /线程返回码参数说明:dwExitCode:指定线程返回码,可以调用GetExitCodeThread()查询返回码的含义.返回值:该函数没有返回值.用法举例:ExitThread(0); /参数0表示要撤销进程中的所有线程3.终止线程TerminateThread()用于终止当前线程.该函数与ExitThread()的区别在于,ExitThread()在撤销线程时将该线程所拥有的资源全部归还给系统,而TerminateThread()不归还资源.原型:BOOL TerminateThread(HANDLE hHandle, /线程句柄DWORD dwExitCode); /线程返回码参数说明:(1)hThread:要终止线程的线程句柄.(2)dwExitCode:指定线程返回码,可以调用GetExitCodeThread()查询返回码的含义.返回值:函数调用成功,将返回一个非0值;若失败,返回0,可以调用函数GetLastError()查询失败的原因.4.挂起线程Sleep()用于挂起当前正在执行的线程.原型:VOID Sleep(DWORD dwMilliseconds);参数说明:dwMilliseconds;指定挂起时间,单位为ms(毫秒).返回值:该函数没有返回值.5.关闭句柄函数CloseHandle()用于关闭已打开的对象的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使线程安全运行.原型:BOOL CloseHandle(HANDLE hObject);参数说明:hObject:已打开对象的句柄.返回值:如果函数调用成功,则返回值为非0值;如果函数调用失败,则返回值为0.若要得到更多的错误信息,调用函数GetLastError()查询.2.1.3实验内容使用系统调用CreatThread()创建一个子线程,并在子线程中显示;Thread is Running!.为了能让用户清楚地看到线程的运行情况,使用Sleep()使线程挂起5s,之后使用ExitThread(0)撤销进程.2.1.4实验要求能正确使用CreatThread(),ExitThread()及Sleep()等系统调用,进一步理解进程与线程理论.2.1.5实验指导本实验在WindowsXP,Microsoft Visual C+ 6.0环境下实现,利用Windows SDK提供的API完成程序的功能.实验在Windows XP环境下安装由于WindowsXP,Microsoft Visual C+ 6.0是一个集成开发环境,其中包含了Windows SDK 所有工具和定义,所以安装了WindowsXP,Microsoft Visual C+ 6.0后不用特意安装SDK.试验中所有的API是操作系统提供的用来进行应用程序开发的系统功能接口.(1)首先启动安装好的,Microsoft Visual C+ 6.0.(2)在,Microsoft Visual C+ 6.0环境下选择File->new命令,然后在Project选项卡中选择Win32 Console Application建立一个控制台工程文件.(3)由于CreatThread()等函数是Microsoft Windows操作系统的系统调用,因此,在下图中选择An application that supports MFC,之后单击Finish按钮.(4)之后将打开,Microsoft Visual C+ 6.0编辑环境(见下图),按本实验的要求编辑C程序,之后编辑,链接并运行该程序即可.2.1.6实验总结在Windows系统中进程是资源的拥有者,线程是系统调用的单位.进程创建后,其主线程也随即被创建.在该实验中,有创建了一个名为ThreadName1的子线程,该子线程与主线程并并发的被系统调度.为了能让用户清楚地看到线程的运行情况,在主线程创建了子线程之后,将主线程挂起5s,以确保子线程能够运行完毕,之后调用ExitThread(0)将所有线程撤销.线程运行如下图所示.2.1.7 源程序/ ThreadCreate.cpp : Defines the entry point for the console application./#include "stdafx.h"#include "ThreadCreate.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endif/ The one and only application objectCWinApp theApp;using namespace std;void ThreadName()printf("Thread is Running!n");static HANDLE hHandle1=NULL; / /需要改成hHandleDWORD ThreadID1; /需要改成ThreadID int _tmain(int argc, TCHAR* argv, TCHAR* envp)int nRetCode = 0; hHandle=CreateThread(LPSECURITY_ATTRIBUTES) NULL,0,(LPTHREAD_START_ROUTINE) ThreadName,(LPVOID)NULL,0,&ThreadID);ThreadName(); /需要在这里添加一个调用ThreadName()1; /需要在这里添加一个调用Sleep(5000);CloseHandle(hHandle);ExitThread(0);return nRetCode; / initialize MFC and print and error on failureif (!AfxWinInit(:GetModuleHandle(NULL), NULL, :GetCommandLine(), 0)/ TODO: change error code to suit your needscerr << _T("Fatal Error: MFC initialization failed") << endl;nRetCode = 1;else/ TODO: code your application's behavior here.CString strHello;strHello.LoadString(IDS_HELLO);cout << (LPCTSTR)strHello << endl;return nRetCode;void ThreaName1()Printf(“Thread is Runing!n”);2.1.8 实验展望可以进一步完善程序功能,请思考以下问题.(1)如何向线程对应的函数传递参数?一个参数如何传递,多个参数又如何传递?(2)深入理解线程与进程的概念,在Windows环境下何时使用进程,何时使用线程?2.2实验二:线程的同步2.2.1 实验目的进一步掌握windows系统环境下线程的创建和撤销熟悉windows系统提供的线程同步API使用windows系统提供的线程同步API解决实际问题2.2.2 实验准备知识:相关API函数介绍2.2.21等待对象等待对象(wait fuctions)函数包括等待一个对象(WaitForSingleObject()和等待多对象(WaitForMultipleObject()两个API函数。等待一个对象WaitForMultipleObject()用于等待一个对象。他等待的对象可以为以下对象之一。Change notification:变化通知。Console input:控制台输入。Events:事件。Job:作业。Mutex:互斥信号量。Process:进程。Semaphore:计数信号量。Thread:线程。Waitable timer:定时器。 原型:DWORD WaitForSingleObject(HANDLE hHandle, /对象句柄DWORD dwMilliseconds /等待时间);参数说明:hHandle:等待对象的对象句柄。该对象句柄必须为SYNCHRONIZE访问。dwMilliseconds:等待时间,单位为ms。若改值为0,函数在测试对象的状态后立即返回,若为INFINITE,函数一直等待下去,直到收到一个信号将其唤醒,如表2-1所示。 返回值:如果返回成功,其返回值说明是何种事件导致函数返回。表2-1 函数描述 访问 描述WAIT_ABANDONED 等待对象的是一个互斥(mutex)对象,该互斥对象没有被拥有它的线程释放,他被设置为不能被唤醒WAIT_OBJECT_0 指定对象被唤醒WAIT_TIMEOUT 超时用法举例:Staitic HANDLE hHandle1=NULL;DWORD dRes;dRes=WaitForSingleObject(hHandle1, 10); /等待对象的句柄为hHandle,等待时间为1000ms等待多个对象WaitForMultipleObject()在指定时间内等待多个对象,他等待的对象与WaitForSingleObject()相同。原型:DWORD WaitForMultipleObject(DWORD nCount, /句柄数组中的句柄数XONST HANDLE *lpHandles, /指向对象句柄数组的指针BOOL fWaitAll, /等待类型DWORD dwMilliseconds /等待时间);参数说明:nCount:由指针*lpHandles指定的句柄数组中的句柄数,最大数是MAXIMUM_WAIT_OBJECTS。*lpHandles:指向对象句柄数组的指针。fWAitAll:等待类型。若存为true,当由lpHandles数组指定的所有对象被唤醒时函数返回;若为FALSE,当由lpHandles数组制定的某一个对象被唤醒时函数返回,且有返回值说明事由哪个对象引起的函数返回。dwMilliseconds :等待时间。单位为ms。若该值为0,函数测试对象的状态后立即返回;若为INFINITE,函数一直等待下去,直到收到一个信号将其唤醒。 返回值:如果成功返回,其返回值说明是何种事件导致函数返回。各参数的描述如表2-2所示。表2-2各参数描述 访问 描述WAIT_OBJECT_0 to (WAIT_OBJECT 若bWaitAll为TRUE,返回值说明所有被等待的对象均被唤醒;若_0+nCount-1) bWaitAll为FALSE。返回值减去WAIT_OBJECT_0说明lpHandles数组下标指定的对象满足等待条件。如果调用时多个对象同时被唤醒,则取多个对象中最小的那个数组下标WAIT_ABANDONED_0 to 若bWaitAll为TRUE,返回值说明所有被等带的对象均被唤醒,并且(WIAT_ABANDONED-0+NcOUNT-1) 至少有一个对象是没有约束的互斥对象;若bWaitAll为FALSE,返回值减去WAIT_ABANDONED_0说明lpHandles数组下标指定的没有约束的互斥对象满足等待条件WAIT_TIMNEOUT 超时且参数bWaitAll指定的条件不能满足信号量对象(semaphore)信号量对象(semaphore)包括创建信号量(CreateSemaphore()打开信号量OpenSemaphore()及增加信号量的值(ReleaseSemaphore()函数。创建信号量CreateSemaphore()用于创建一个信号量。原型:HANDLE CreateSemaphore(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,/安全属性LONG lInitialCount, /信号量对象初始值LONG liMaximumCount, /信号量最大值LPCTSTR lpName/信号量名);参数说明:lpSemaphoreAttributes:指定安全属性,为null是,信号量得到一个默认的安全描述符。lInitialCount:指定信号量对象的初始值。该值必须大于等于0,小于等于lMaximumCount 。当其值大于0是,信号量被唤醒。当该函数释放了一个等待该信号量的线程时,lInitialCount值减1,当调用函数ReleaseSemaphore()时,按其指定的数量加一个值。lMaximumCount:指出该信号量的最大值,该值必须大于0.lpName:给出该信号量的名字。返回值:信号量创建成功,将返回该型号量的句柄。如果给出的信号量名是系统已经存在的信号量,将返回这个已经存在的信号量的句柄。如果失败,系统返回null,还可以调用函数GEtLastError()查询失败的原因。用法举例:Static HANDLE hHandle1=null;/创建一个信号量,其初值为0,最大值为5,信号量的名字为“SemphoreName1”hHnadle1= CreateSemaphore(NULL,SemphoreName1); 打开信号量OpenSemaphore()用于打开一个信号量。原型:HANDLEOpenSemaphore(DWORDdwDesidedAccess, /访问标志BOOL bInheritHandle, /继承标志 LPCTSTRlpNme /信号量名);参数说明:(1).dwDesiredAccess:指出打开后要对信号量进行何种访问,如表所示。表 2-3 访问状态访问 描述SEMAPHORE_ALL_ACCESS 可以进行任何对信号量的访问SEMPHORE_MODFIY_STATE 可以使用ReleaseSemaphore()修改信号量的值,使信号量的值成为可用状态SYNCHRONIZE 使用等待函数(wait functions),等待信号量成为可用状态(). bInheritHandle:指出返回的的信号量句柄是否可以继承。().lpName:给出信号量的名字返回值:信号量打开成功,将返回信号量的句柄;如果失败,系统返回null,可以调用函数GetLastError()查询失败的原因。;用法举例:Static HANDLE hHandle1=null;/打开一个名为 ” SemphoreName1 ” 的信号量,之后可使用ReleaseSemaphore()函数增加信号量的值hHandle1=OpenSemaphore(SEMAPHORE_MOFDIFY_START,NULL, ” SemphoreName1” );3. 增加信号量的值ReleaseSemaphore()用于增加信号量的值。原型:BOOL ReleaseSemaphore(HANDLE hSemaphore, /信号量对象句柄LONG lReleaseCount, /信号量要增加数值LPLONG lpPreiousCount /信号量要增加数值地址);参数说明:(1).hSemaphore:创建或打开信号量时给出的信号量对象句柄。Windows NT中建议使用SEMAPHORE_MODIFY_STARTE访问属性打开该信号量。(2). lReleaseCount:信号量要增加数值。该值必须大于0。如果增加该值后大于信号创建时给出的lMaximumCount值,则增加操作失效,函数返回FALSE。(3). lpPreiousCount :接收信号量的一个32位的一个变量。若不需要接受该值,可以指定为null。返回值: 如果成功,将返回一个非0值;如果失败,系统返回一个0,可以调用一个GetLastError()查询失败的原因。用法举例:Static HANDLE hHandle1=NULL;BOOL rc;rc= ReleaseSemaphore(hHandle1,1,NULL);/给信号量的值加1;2.2.3实验内容完成主子两个线程之间的同步,要求子线程先执行。在主线程中使用系统调用CreateThread()创建一个子线程。主线程创建一个子线程后进入阻塞状态,直到子线程运行完毕后唤醒主线程。2.2.4实验要求能正确使用等待对象、WaitForSingleObject()或WaitForMultipleObject(0及信号量对象CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()等系统调用,进一步理解线程的同步。2.2.5实验指导具体操作过程同本章实验一,在Microsoft visual C+6.0环境下建立一个MFC支持的控制台文件,编写C程序,在程序中使用CreateSemaphore(NULL,0,1,”SemaphoreName1”)创建一个名为“SemaphoreName1”的信号量,信号量的初始值为0,之后使用OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE,NULL, ”SemaphoreName1)打开该信号量,这里访问标志使用“SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE”,以便之后可以使用WaitForSingleObject()等待该信号量及使用ReleaseSemaphore()释放该信号量,然后创建一个子线程,主线程创建子线程后调用WaitForSingleObject(hHandle1,INFINITE),这里等待时间设置为INFINITE表示一直等待下去,直到该信号量被唤醒为止。子线程结束,调用ReleaseSemaphore(hHandle1,1,NULL)释放信号量,使信号量的值加1。2.2.6实验总结实验完成了主、子线程的同步,主线程创建子线程后,主线程塞,让子线程先执行,等子线程执行完后,由子线程唤醒子线程。主子线程运行情况如图: 主、子线程的运行情况2.2.7 源程序2.2.7 源程序/ Semaphore.cpp : Defines the entry point for the console application./#include "stdafx.h"#include " Semaphore.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE = _FILE_;#endif/ The one and only application objectCWinApp theApp;using namespace std;static HANDLE h1; /线程句柄static HANDLE hHandle1=NULL; /信号量句柄void func();int _tmain(int argc, TCHAR* argv, TCHAR* envp)int nRetCode = 0;DWORD dwThreadID1;DWORD dRes,err;hHandle1=CreateSemaphore(NULL,0,1,"SemaphoreName1");/创建一个信号量if (hHandle=NULL)printf("Semaphore Create!n");else printf("Semaphore Create Success!n");hHandle1=OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE,NULL,"SemaphoreName1"); /打开信号量if (hHanle1=NULL) printf("Semaphore Open Fail!n");else printf("Semaphore Open Success!n");h1=CreateThread(LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)func,(KPVOID)NULL,0,&dwThreadID1); /创建子线程if (h1=NULL) prienf("Thread1 create Fail!n");else printf("Thread1 create Success!n");dRes=WaitForSingleObject(hHandle1,INFINITE); /主线程等待子线程结束err=GetLastError();printf("WaitForSingleObject err = %dn",err);if (dRes=WAIT_TIMEOUT) printf("TIMEOUT!dRes=%dn",dRes);else if (dRes=WAIT_OBJECT_0) printf("WAIT_OBJECT!dRes=%dn",dRes);else if (dRes=WAIT_ABANDONED) printf("WAIT_ABANDONED!Dres=%dn",dRes);else printf("dRes =%dn",dRes);CloseHand(h1);CloseHandle(hHandle1);ExitThread(0);return nRetCode;void func()BOOL rc;DWORD err;Printf("Now In Thread!n");rc =ReleaseSemaphore(hHandle1,1,NULL);err =GetLastError(); /子线程唤醒主线程printf("ReleaseSemaphore err=%dn",err);if(rc=0) printf("Semaphore Release Fail!n");else printf("Semaphore Release Success!rc= %dn",rc);2.2.8实验展望上面的程序完成了主、子两个线程执行先后顺序的同步关系,思考以下问题。如何实现多个线程的同步?若允许子线程执行多次后主线程再执行,又如何设置信号量的初值?实验三:线程的互斥2.3.1实验目的(1)熟练掌握Windows系统环境下线程的创建与撤销。(2)熟悉Windows系统提供的线程互斥API。(3)使用Windows系统提供的线程互斥API解决实际问题。2.3.2 实验准备知识:相关API函数介绍2.3.2.1临界区对象临界区对象(CriticalSection)包括初始化临界区(InitializeCriticalSection()、进入临界区(EnterCriticalSection()、退出临界区(LeaveCriticalSection()及删除临界区(DeleteCriticalSection()灯API函数。初始化临界区InitializeCriticalSection()用于初始化临界区对象。原型:VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection );参数说明:lpCriticalSection:指出临界区对象的地址。返回值:该函数没有返回值。用法举例:LPCRITCAL_SECTION hCriticalSection;CRITICAL_SECTION Critical;hCriticalSection=&Critical;InitializeCriticalSection(hCriticalSection);进入临界区EnterCriticalSection()等待进入临界区的权限,当获得该权限后进入临界区。原型:VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);参数说明:lpCriticalSection:指出临界区对象的地址。返回值:该函数没有返回值。用法举例:LPCRITICAL_SECTION hCriticalSection;CRITICAL_SECTION Critical;hCriticalSection=&Critical;EnterCriticalSection(hCriticalSection);退出临界区LeaveCriticalSection()释放临界区的使用权限。原型:VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);参数说明:lpCriticalSection:指出临界区对象的地址。返回值:该函数没有返回值用法举例:LPCRITICAL_SECTION hCriticalSection;CRITICAL_SECTION Critical;hCriticalSection=&Critical;LeaveCriticalSection(hCriticalSection);删除临界区DeleteCriticalSection()删除与临界区有关的所有系统资源。原型:VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);参数说明:lpCriticalSection:指出临界区对象的地址。返回值:该函数没有返回值。用法举例:LPCRITICAL_SECTION hCriticalSection;CRITICAL_SECTION Critical;hCriticalSection=&Critical;DeleteCriticalSection(hCriticalSection);互斥对象互斥对象(Mutex)包括创建互斥对象(CreateMutex()、打开互斥对象(OpenMutex()及释放互斥对象(ReleaseMutex()API函数。创建互斥对象CreateMutex(0用于创建一个互斥对象。原型:HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,BOOL bInitialOwner,LPCTSTR lpName);参数说明:lpMutexAttributes:指定安全属性,为NULL时,信号量得到一个,默认的安全描述符。bInitialOwner:指定初始的互斥对象。如果该值为TRUE并且互斥对象已经纯在,则调用线程获得互斥对象的所有权,否则调用线程不能获得互斥对象的所有权。想要知道互斥对象是否已经存在,参见返回值说明。lpName:给出互斥对象的名字。返回值:互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的互斥对象,将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调用函数GetLastError()查询失败的原因。用法举例:static HANDLE hHandle1=NULL;/常见一个名为"MutexName1"的互斥对象hHandle1=CreateMutex(NULL,FALSE, "MutexName1");打开互斥对象OpenMutex()用于打开一个互斥对象。原型:HANDLE OpenMutex(DWORD dwDesiredAccess,BOOL bInheritHandle,LPCTSTR lpName);参数说明:指明系统安全属性支持的对互斥对象所有可能的访问。如果系统安全属性不支持,则不能获得对互斥对象访问权。(1)dwDesireAccess:指出发开后要对互斥对象进行何种访问,具体描述如表2-4所示。 表 2-4 对互斥对象的访问种类 访问 描述 MUTEX_ALL_ACCESS 可以进行对任何互斥对象的访问 SYNCHRONIZE 使用等待函数 wait functions 等待互斥对象成为可用状态或使用 ReleaseMutex()释放使用权,从而获得互斥对象的使用权(2) bInheritHandle: 指出返回信号量的句柄是否可以继承。(3) IpName : 给出信号量的名字。返回值互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调用函数 GetLastError()查询失败的原因。用法举例: static HANDLE hHandle1 = NULL;/打开一个名为"MutexName1"的互斥对象hHandle=OpenMutex(SYNCHRONIZE,NULL,"MutexName1");3. 释放互斥对象ReleaseMutex() 用于释放互斥对象。原型:BOOL ReleaseMutex(HANDLE;);参数说明:Mutex对象的句柄。CreateMutex()和OpenMutex()函数返回该句柄。返回值:如果成功,将返回一个非0值;如果失败系统将返回0,可以调用函数 GetLastError()查询失败的原因。用法举例:static HANDLE hHandle1=NULL;BOOL rc;rc= ReleaseMutex(hHandle1)2.3.3 实验内容完成两个子线程之间的互斥。在主线程中使用系统调用 CreleaseThread()创建两个子线程,并使两个子线程互斥的使用全局变量 count。2.3.4 实验要求 能正确的使用临界区对象,包括初始化临界区 InitializeCriticalSection()、进入临界区EnterCriticalSection()、退出临界区 LeaveCritical()及删除临界区 DeleteCriticalSection() 进一步理解线程的互斥。2.3.5 实验指导具体操作过程同实验一,在 Microsoft Visual C+ 6.0 环境下建立一个MFC支持的控制台工程文件,编写C程序,在主线程中使用InitializeCriticalSection()初始化临界区,然后建立两个子线程,在两个子线程中使用全局变量 count 的前、后分别使用EnterCriticalSeation()进入临界区LeaveCriticalSection()退出临界区,等两个子线程运行完毕,主线程使用DeleteCriticalSection()删除临界区并撤销线程。2.3.6 实验总结该试验完成了两个子线程之间的互斥。若去掉互斥对象,观察全局变量count的变化,了解互斥对象的作用,进一步理解线程的互斥。本试验也可以使用互斥对象(Mutex)来完成两个线程的互斥,互斥对象(Mutex)的使用方法与信号量对象相似,这里不再说明,请同学自己完成。线程互斥访问全局变量 count 如图 2-5所示。2.3.7 源程序/Mutex.cpp:Defines the entry point for the console application./#include “stdafx.h”#include “Mutex.h”#ifdef_DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE =_FILE_;#endif/The one and only application objectCWinApp theApp;Using namespace std;static int count =5;static HANDLE h1;static HANDLE h2;LPCRITICAL_SECTION hCriticalSection; /定义指向临界区对象的地址指针CRITICAL_SECTION Critical; /定义临界区void func1();void func2();Int_tmain(int argc,TCHAR*argv,TCHAR*envp)int nRetCode=0; DWORD dwThreadID1, dwThreadID2;HCriticalSection=&Critical; /将指向临界区对象的指针指向临界区InitializeCriticalSection(hCriticalSection); /初始化临界区h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)func1,(LPVOID)NULL,0,&dwThreadID1); /创建线程 func1if (h1=NULL) printf(”Thread1 create FAIL!n”);else printf(”Thread1 create Success!n”);h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,0,(LPTHREAD_START_ROUTINE)func2,(LPVOID)NULL, 0,&dwThreadID2); /创建线程 func2if (h2=NULL) printf(”Thread2 create FAIL!n”);else printf(”Thread2 create Success!n”)Sleep(1000);CloseHandle(h1);CloseHandle(h2);DeleteCriticalSection(hCriticalSection); /删除临界区ExitThread(0);Return nRetCode;void func2() int r2; EnterCriticalSection (hCriticalSection); /进入临界区 r2=count; _sleep(100);r2=r2+1;count=r2;printf(“count in func2=%dn”,count);LeaveCriticalSection (hCriticalSection); /退出临界区void func1() int r1; EnterCriticalSection (hCriticalSection); /进入临界区 R1=count; _sleep(500);r1=r1+1;count=r1;printf(“count in func1=%dn”,count);LeaveCriticalSection (hCriticalSection); /退出临界区2.3.8 实验展望上面的实验是使用临界区对象(CriticalSection)实现的,同学们可以使用互斥对象(Mutex)来完成。在完成以上三个实验后,同学们对Windows系统提供的线程创建与撤销、线程的同步与互斥、API有了一定的了解,在此基础上设计并完成一个综合性的实验解决实际同步与互斥问题,如生产者与消费者问题、读者与写者问题等。饰演的题目可以自行设计,但要求必须涉及县城的创建与撤销、等待对象函数(waitFunctions)、信号量对象(Semaphore)临界区对象(CriticalSection)或互斥对象(Mutex)的使用。2.4实验四:使用命名管道实现进程通信2.4.1 实验目的(1)了解windows系统环境下的进程通讯机制。(2)熟悉Windows系统提供的进程通信API。2.4.2 实验准备知识:相关API函数介绍1.建立命名管道函数CreateNamePipe()创建一个命名管道实例,并返回该管道的句柄。原型: HANDLE CreateNamePipe( LPCTSTR lpName, /命名管道的名字 DWORD dwOpenMode, /命名管道的访问模式 DWORD dwPipeMode, /命名管道的模式 DWORD nMaxInstances, /可创建实例的最大值 DWORD nOutBufferSize, /以字节为单位的输出缓冲区的大小 DWORD nInBufferSize, /以字节为单位的输入缓冲区的大小 DWORD nDefaultTimeOut, /默认超时时间 LPSECURITY_ATTRIBUTES lpSecurityAttributes /安全属性 );参数说明:lpName:为命名管道的名字,管道的命名方式为ServerNamepipepipename,其中ServerName为用命名管道通信时服务器的主机名或IP地址,pipename为命名管道的名字,用户可自行定义。dwOpenMode:指出命名管道的访问模式。模式如表2-5所示。dwPipeMode:指出管道的模式。模式如表2-6所示、nMaxInstances:该命名管道可以创建实例的最大值。nOutBufferSize:输出缓冲区的大小,以字节为单位。nInBufferSize:输入缓冲区的大小,以字节为单位。nDefaultTimeOut:默认的超时时间,以ms为单位。如果函数WaitNamePipe()指出NMWAIT_USE_DEFAULT_WAIT,每个管道实例必须指定同一值的名字。lpSecurityAttributes:为管道指定安全属性,为NULL时,管道得到一个默认的安全描述符。返回值:如果管道创建成功,将返回服务器命名管道实例的句柄。如果失败,返回INVALID_HANDLE_VALUE,可以调用函数GetLastError()查询失败的原因;当返回ERROR_INVALID_PARAMETER时,表明参数nMaxInstances指定的值大于PIPE_UNLIMTED_INSTANCES。2.连接命名管道服务器用函数ConnectNamePipe()连接命名管道。创建后命名管道也等待客户端的连接,客户端可以使用函数CreateFile()和CallNamedPipe()进行连接。原型:BOOL ConnectNamedPipe(HANDLE hNamePipe, /命名管道实例句柄LPOVERLAPPED lpOver lapped /指向Overlapped结构的指针);参数说明:hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。lpOverlapped:指向Overlapped结构的指针,可设其为NULL。返回值:成功,将返回一个非
展开阅读全文