版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第二章windows的进程管理
2.1实验一:线程的创建与撤销
2.1.1实验目的
(1)熟悉windows系统提供的线程创建与撤销系统调用.
(2)掌握windows系统环境下线程的创建与撤销方法.
2.1.2实验准备知识
1.线程的创建
CeateThread。完成线程的创建.它在调用进程的地址空间上创建•个线程,执行指定的函
数,并返回新建立的线程的句柄.
原型:
HANDLECeateThread(
LPSECURITY_ATTRIBUTESIpThreadAttributes,
DWORDdwStackSize,
LPSECURITY_START_ROUTINEIpStartAddress,
LPVOIDIpparameter,
DWORDdwCreationFlags,
LPDWORDIpThreadld
);
参数说明:
⑴IpThreadAttributes:为线程指定安全属性.为NULL时,线程得到一个默认的安全描述符.
(2)dwStackSize:线程堆栈的大小淇值为0时,其大小与调用该线程的线程堆栈大小相同.
⑶IpStartAddress:指定线程要执行的函数.
⑷Ipparameter:函数中要传递的参数.
⑸dwCreationFlags:指定线程创建后所处的状态.若为CRRATE_SUSPENDED,表示创建后出
于挂起状态,用ResumeThread。激活线程才可以执行.若该值为0,表示线程创建后立即执行.
⑹IpThreadld:用一个32位的变量接受系统返回的线程标识符.若该值设为NULL,系统不
返回线程标识符.
返回值:
如果线程创建成功,将返回线程的句柄;如果失败,系统返回NULL,可以调用函数
GetLastError查询失败的原因.
用法举例:
staticHANDLEhHandlel=NULL;〃用于存储线程返回句柄的变量
DWORDdwThreadlDl;〃用于存储线程标识符的变量
〃创建一个名为ThreadNamel的线程
hHandlel=CeateThread((LPSECURITY_ATTRIBUTES))NULL
0,
(LPSECURITY_START_ROUTINE)ThreadNamel,
(LPDWORD)NULL,
0,&dwThreadlDl);
2.撤销进程
ExitThread。用于撤销当前进程.
原型:
VOIDExitThread(
DWORDdwExitCode);〃线程返回码
参数说明:
dwExitCode:指定线程返回码,可以调用GetExitCodeThread。查询返回码的含义.
返回值:
该函数没有返回值.
用法举例:
ExitThread(O);〃参数0表示要撤销进程中的所有线程
3.终止线程
TerminateThread。用于终止当前线程.该函数与ExitThread。的区别在于,ExitThread()在撤
销线程时将该线程所拥有的资源全部归还给系统,而TerminateThreadO不归还资源.
原型:
BOOLTerminateThread(
HANDLEhHandle,〃线程句柄
DWORDdwExitCode);〃线程返回码
参数说明:
⑴hThread:要终止线程的线程句柄.
⑵dwExitCode:指定线程返回码,可以调用GetExitCodeThread()查询返回码的含义.
返回值:
函数调用成功,将返回一个非0值;若失败,返回0,可以调用函数GetLastError()查询失败
的原因.
4.挂起线程
Sleep。用于挂起当前正在执行的线程.
原型:
VOIDSleep(DWORDdwMilliseconds);
参数说明:
dwMilliseconds;指定挂起时起单位为ms(毫秒).
返回值:
该函数没有返回值.
5.关闭句柄
函数CloseHandle()用于关闭已打开的对象的句柄淇作用与释放动态申请的内存空间类
似,这样可以释放系统资源,使线程安全运行.
原型:
BOOLCloseHandle(HANDLEhObject);
参数说明:
hObject:已打开对象的句柄.
返回值:
如果函数调用成功,则返回值为非。值;如果函数调用失败,则返回值为0.若要得到更多的
错误信息,调用函数GetLastError。查询.
2.1.3实验内容
使用系统调用CreatThread()创建一个子线程,并在子线程中显示;ThreadisRunning!.为了
能让用户清楚地看到线程的运行情况,使用Sleep。使线程挂起5s,之后使用ExitThread⑼撤销
进程.
2.1.4实验要求
能正确使用CreatThread。,ExitThread。及Sleep。等系统调用,进•步理解进程与线程理论.
2.1.5实验指导
本实验在WindowsXP,MicrosoftVisualC++6.0环境下实现,利用WindowsSDK提供的API
完成程序的功能.实验在WindowsXP环境下安装由于WindowsXP,MicrosoftVisualC++6.0是
一个集成开发环境,其中包含了WindowsSDK所有工具和定义,所以安装了
WindowsXP,MicrosoftVisualC++6.0后不用特意安装SDK.试验中所有的API是操作系统提供
的用来进行应用程序开发的系统功能接口.
⑴首先启动安装好的,MicrosoftVisualC++6.0.
(2)在,MicrosoftVisualC++6.0环境下选择File->new命令,然后在Project选项卡中选择
Win32ConsoleApplication建立一个控制台工程文件.
⑶由于CreatThread()等函数是Micros。代Windows操作系统的系统调用,因此,在下图中
选择AnapplicationthatsupportsMFC,之后单击Finish按
钮.
⑷之后将打开,MicrosoftVisualC++6,0编辑环境(见下图),按本实验的要求编辑C程序,之
后编辑,链接并运行该程序即可.
2.1.6实验总结
在Windows系统中进程是资源的拥有者,线程是系统调用的单位.进程创建后,其主线程
也随即被创建.在该实验中,有创建了一个名为ThreadNamel的子线程,该子线程与主线程并
并发的被系统调度.为了能让用户清楚地看到线程的运行情况,在主线程创建了子线程之后,
将主线程挂起5s,以确保子线程能够运行完毕,之后调用ExitThread(O)将所有线程撤销.线程运
行如下图所示.
2.1.7源程序
//ThreadCreate.cpp:Definestheentrypointfortheconsoleapplication.
//
include"stdafx.h"
#include"ThreadCreate.h"
#ifdef_DEBUG
#definenewDEBUG_NEW
#undefTHIS_FILE
staticcharTHIS_FILE[]=_FILE_;
#endif
lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
//Theoneandonlyapplicationobject
CWinApptheApp;
usingnamespacestd;
voidThreadName()
(
printf(HThreadisRunning!\n");
)
staticHANDLEhHandlel=NULL;〃〃需要改成hHandle
DWORDThreadIDl;〃需要改成ThreadID
int_tmain(intargc,TCHAR*argv[],TCHAR*envp[])
(
intnRetCode=0;
hHandle=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)ThreadName/
(LPVOID)NULL,
0,
&ThreadlD);
ThreadName();〃需要在这里添加一个调用
ThreadName()l;〃需要在这里添加一个调用
Sleep(SOOO);
CloseHandle(hHandle);
ExitThread(O);
returnnRetCode;
//initializeMFCandprintanderroronfailure
if(!AfxWinlnit(::GetModuleHandle(NULL),NULL,::GetCommandLine(),0))
{
//TODO:changeerrorcodetosuityourneeds
cerr«_T("FatalError:MFCinitializationfailed")«endl;
nRetCode=1;
)
else
(
//TODO:codeyourapplication'sbehaviorhere.
CStringstrHello;
strHello.LoadString(IDS_HELLO);
cout«(LPCTSTR)strHello«endl;
}
returnnRetCode;
voidThreaNamel()
(
Printf("ThreadisRuning!\n");
)
2.1.8实验展望
可以进一步完善程序功能,请思考以下问题.
⑴如何向线程对应的函数传递参数?一个参数如何传递,多个参数又如何传递?
(2)深入理解线程与进程的概念,在Windows环境下何时使用进程,何时使用线程?
2.2实验二:线程的同步
2.2.1实验目的
进一步掌握windows系统环境下线程的创建和撤销
熟悉windows系统提供的线程同步API
使用windows系统提供的线程同步API解决实际问题
2.2.2实验准备知识:相关API函数介绍
2.2.2.1等待对象
等待对象(waitfactions)函数包括等待一个对象(WaitForSingleObject())和等待多对
象(WaitForMultipleObject())两个API函数。
等待一个对象
WaitForMultipleObject()用于等待一个对象。他等待的对象可以为以下对象之一。
Changenotification:变化通知。
Consoleinput:控制台输入。
Events:事件。
Job:作业。
Mutex:互斥信号量。
Process:进程。
Semaphore:计数信号量。
Thread:线程。
Waitabletimer:定时器。
原型:
DWORDWaitForSingleObject(
HANDLEhHandle,〃对象句柄
DWORDdwMilliseconds〃等待时间
);
参数说明:
hHandle:等待对象的对象句柄。该对象句柄必须为SYNCHRONIZE访问。
dwMilliseconds:等待时间,单位为ms。若改值为0,函数在测试对象的状态后立即返
回,若为INFINITE,函数一直等待下去,直到收到一个信号将其唤醒,如表2-1所示。
返回值:
如果返回成功,其返回值说明是何种事件导致函数返回。
表2-1函数描述
WAITABANDONED等待对象的是•个互斥(mutex)对象,该互斥对象没有被拥
有它的线程释放,他被设置为不能被唤醒
WAIT_OBJECT_0指定对象被唤醒
WAIT_TIMEOUT超时
用法举例:
StaiticHANDLEhHandlel=NULL;
DWORDdRes;
dRes=WaitForSingleObject(hHandlel,10);〃等待对象的句柄为hHandle,等待时间为
1000ms
等待多个对象
WaitForMultipleObject()在指定时间内等待多个对象,他等待的对象与
WaitForSingleObject()相同。
原型:
DWORDWaitForMultipleObject(
DWORDnCount,〃句柄数组中的句柄数
XONSTHANDLE*lpHandles,〃指向对象句柄数组的指针
BOOLfWaitAII,〃等待类型
DWORDdwMilliseconds〃等待时间
);
参数说明:
nCount:由指针*lpHandles指定的句柄数组中的句柄数,最大数是
MAXIMUM_WAIT_OBJECTS«
*lpHandles:指向对象句柄数组的指针。
fWAitAII:等待类型。若存为true,当由IpHandles数组指定的所有对象被唤醒时函数返
回;若为FALSE,当由IpHandles数组制定的某一个对象被唤醒时函数返回,且有返回值说
明事由哪个对象引起的函数返回。
dwMilliseconds:等待时间。单位为ms。若该值为0,函数测试对象的状态后立即返回;
若为INFINITE,函数一直等待下去,直到收到一个信号将其唤醒。
返回值:
如果成功返回,其返回值说明是何种事件导致函数返回。
各参数的描述如表2-2所示。
表2-2各参数描述
m赢
WAIT_OBJECT_0to(WAIT_OBJECT芥bWaitAII为TRUE,返回值说明所有被等待的对象均
被唤醒;若
_0+nCount-l)bWaitAII为FALSE«返回值减去WAIT_OBJECT_0说明
IpHandles数组下标指定的对象满足等待条件。如果调用时多个对象同时被唤醒,则取多个
对象中最小的那个数组下标
WAIT_ABANDONED_0to若bWaitAII为TRUE,返回值说明所有被等带的对象
均被唤醒,并且(WIAT_ABANDONED-0+NcOUNT-l)至少有一个对象是没有约束的互斥
对象;若bWaitAII
为FALSE,返回值减去WAIT_ABANDONED_0说明IpHandles数组下标指定的没有约束的互斥
对象满足等待条件
WAIT_TIMNEOUT超时且参数bWaitAII指定的条件不能满足
信号量对象(semaphore)
信号量对象(semaphore)包括创建信号量(CreateSemaphore())打开信号量
OpenSemaphore()及增加信号量的值(ReleaseSemaphore())API函数。
创建信号量
CreateSemaphore()用I于创建一个信号量。
原型:
HANDLECreateSemaphore(
LPSECURITY_ATTRIBUTESIpSemaphoreAttributes,〃安全属性
LONGIlnitialCount,〃信号量对象初始值
LONGliMaximumCount,〃信号量最大值
LPCTSTRIpName〃信号量名
);
参数说明:
IpSemaphoreAttributes:指定安全属性,为null是,信号量得到-个默认的安全描述符。
IlnitialCount:指定信号量对象的初始值。该值必须大于等于0,小于等于
iMaximumCount„当其值大于0是,信号量被唤醒。当该函数释放了一个等待该信号量的
线程时,IlnitialCount值减1,当调用函数ReleaseSemaphore。时,按其指定的数量加一个值。
IMaximumCount:指出该信号量的最大值,该值必须大于0.
IpName:给出该信号量的名字。
返回值:
信号量创建成功,将返回该型号量的句柄。如果给出的信号量名是系统已经存在的信号
量,将返回这个已经存在的信号量的句柄。如果失败,系统返回null,还可以调用函数
GEtLastError()查询失败的原因。
用法举例:
StaticHANDLEhHandlel=null;
〃创建一个信号量,其初值为0,最大值为5,信号量的名字为“SemphoreNamel”
hHnadlel=CreateSemaphore(NULL,0,5,"SemphoreNamel");
打开信号量
OpenSem叩hore()用于打开一个信号量。
原型:
HANDLEOpenSem叩hore(
DWORDdwDesidedAccess,〃访问标志
BOOLblnheritHandle,〃继承标志
LPCTSTRIpNme〃信号量名
);
参数说明:
(1).dwDesiredAccess:指出打开后要对信号量进行何种访问,如表2—3所示。
表2-3访问状态
访问描述
SEMAPHORE_ALL_ACCESS可以进行任何对信号量的访问
SEMPHORE_MODFIY_STATE可以使用ReleaseSemaphore()修改信号量的值,使信号量
的值成为可用状态
SYNCHRONIZE使用等待函数(waitfunctions),等待信号量成为可用状态
(2).blnheritHandle:指出返回的的信号量句柄是否可以继承。
(3).IpName:给出信号量的名字
返回值:
信号量打开成功,将返回信号量的句柄;如果失败,系统返回null,可以调用函数
GetLastError()查询失败的原因。;
用法举例:
StaticHANDLEhHandlel=null;
〃打开一个名为"SemphoreNamel”的信号量,之后可使用ReleaseSemaphore()函
数增加信号量的值
hHandlel=OpenSemaphore(SEMAPHORE_MOFDIFY_STAR1;NULL,"
SemphoreNamel");
3.增加信号量的值
ReleaseSemaphore()用于增加信号量的值。
原型:
BOOLReleaseSemaphore(
HANDLEhSemaphore,〃信号量对象句柄
LONGIReleaseCount,〃信号量要增加数值
LPLONGIpPreiousCount〃信号量要增加数值地址
);
参数说明:
(1).hSemaphore:创建或打开信号量时给出的信号量对象句柄。WindowsNT中建议
使用SEMAPHORE_MODIFY_STARTE访问属性打开该信号量。
(2).IReleaseCount:信号量要增加数值。该值必须大于0。如果增加该值后大于信号
创建时给出的iMaximumCount值,则增加操作失效,函数返回FALSE。
(3).IpPreiousCount:接收信号量的一个32位的一个变量。若不需要接受该值,可
以指定为nullo
返回值:
如果成功,将返回一个非0值;如果失败,系统返回一个0,可以调用一个GetLastError
()查询失败的原因。
用法举例:
StaticHANDLEhHandlel=NULL;
BOOLrc;
rc=ReleaseSemaphore(hHandlel,1,NULL);〃给信号量的值加1;
2.2.3实验内容
完成主子两个线程之间的同步,要求子线程先执行。在主线程中使用系统调用
CreateThread()创建一个子线程。主线程创建一个子线程后进入阻塞状态,直到子线程运
行完毕后唤醒主线程。
2.2.4实验要求
能正确使用等待对象、WaitForSingleObject()或WaitForMultipleObject(0及信号量对
象CreateSemaphore()、OpenSemaphore()、Releasesemaphore()等系统调用,进一步
理解线程的同步。
2.2.5实验指导
具体操作过程同本章实验一,在MicrosoftvisualC++6.0环境下建立一个MFC支持的控
制台文件,编写C程序,在程序中使用CreateSemaphore(NULL,0,1,"SemaphoreNamel")
创建•个名为"SemaphoreNamel”的信号量,信号量的初始值为0,之后使用OpenSemaphore
(SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE,NULL,"SemaphoreNamel)打开该信号量,这
里访问标志使用“SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE”,以便之后可以使用
WaitForSingleObject()等待该信号量及使用Releasesemaphore()释放该信号量,然后创
建一个子线程,主线程创建子线程后调用WaitForSingleObject(hHandlel,INFINITE),这里
等待时间设置为INFINITE表示一直等待下去,直到该信号量被唤醒为止。子线程结束,调用
Releasesemaphore(hHandlel,1,NULL)释放信号量,使信号量的值加1。
2.2.6实验总结
实验完成了主、子线程的同步,主线程创建子线程后,主线程塞,让子线程先执行,等
子线程执行完后,由子线程唤醒子线程。主子线程运行情况如图:
D回汉
密C:\Windows\system32\cmd.exe
SemaphoreCreateSuccess?
SemaphoreOpenSuccess?
IhreadCreateSuccess?
NoviInThread?
WaitForSingleObjecterr=0
l/AIT_OBJECT?=dres=0
ReleaseSemphoreerr=0
SenphoreReleaseSuccessrc=1
Pressanykeytocontinue
主、子线程的运行情况
2.2.7源程序
2.2.7源程序
//Semaphore.cpp:Definestheentrypointfortheconsoleapplication.
//
include"stdafx.h"
include"Semaphore.h"
#ifdef_DEBUG
#definenewDEBUG_NEW
#undefTHIS_FILE
staticcharTHIS_FILE[]=_FILE_;
#endif
〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃/
//Theoneandonlyapplicationobject
CWinApptheApp;
usingnamespacestd;
staticHANDLEhl;〃线程句柄
staticHANDLEhHandlel=NULL;〃信号量句柄
voidfunc();
int_tmain(intargc,TCHAR*argv[],TCHAR*envp[])
(
intnRetCode=0;
DWORDdwThreadIDl;
DWORDdRes,err;
,一个信号量
hHandlel=CreateSemaphore(NULL/Ozl,"SemaphoreNamel");//^J®
if(hHandle==NULL)printf("SemaphoreCreate!\n");
elseprintf("SemaphoreCreateSuccess!\n");
hHandlel=OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE,
NULL,
"SemaphoreNamel");〃打开信号量
if(hHanlel==NULL)printf("SemaphoreOpenFail!\n");
elseprintf("SemaphoreOpenSuccess!\n");
hl=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func,
(KPVOID)NULL,
0,&dwThreadlDl);〃创建子线程
if(hl==NULL)prienf("ThreadlcreateFail!\n");
elseprintf(uThreadlcreateSuccess!\n");
dRes=WaitForSingleObject(hHandlel,INFINITE);〃主线程等待子线程结束
err=GetLastError();
printf("WaitForSingleObjecterr=%d\n"zerr);
if(dRes==WAIT_TIMEOUT)printf("TIMEOUT!dRes=%d\n"zdRes);
elseif(dRes==WAIT_OBJECT_0)printf("WAIT_OBJECT!dRes=%d\n",dRes);
elseif(dRes==WAIT_ABANDONED)
,
printf("WAIT_ABANDONED!Dres=%d\n'/dRes);
elseprintf("dRes=%d\n"zdRes);
CloseHand(hl);
CloseHandle(hHandlel);
ExitThread(O);
returnnRetCode;
voidfunc()
(
BOOLre;
DWORDerr;
Printf("NowInThread!\n");
rc=ReleaseSemaphore(hHandlel,l,NULL);
err=GetLastError();〃子线程唤醒主线程
printf("ReleaseSemaphoreerr=%d\n",err);
if(rc==O)printf(HSemaphoreReleaseFail!\nH);
elseprintf("SemaphoreReleaseSuccess!rc=%d\n",rc);
)
2.2.8实验展望
上面的程序完成了主、子两个线程执行先后顺序的同步关系,思考以下问题。
如何实现多个线程的同步?
若允许子线程执行多次后主线程再执行,又如何设置信号量的初值?
实验三:线程的互斥
2.3.1实验目的
(1)熟练掌握Windows系统环境下线程的创建与撤销。
(2)熟悉Windows系统提供的线程互斥API。
(3)使用Windows系统提供的线程互斥API解决实际问题。
2.3.2实验准备知识:相关API函数介绍
23.2.1临界区对象
临界区对象(CriticalSection)包括初始化临界区(InitializeCriticalSection())、进入临界
区(EnterCriticalSection())、退出临界区(LeaveCriticalSection())及删除临界区
(DeleteCriticalSection())灯API函数。
初始化临界区
InitializeCriticalSection()用于初始化临界区对象。
原型:
VOIDInitializeCriticalSection(
LPCRITICAL__SECTIONIpCriticalSection
);
参数说明:
IpCriticalSection:指出临界区对象的地址。
返回值:
该函数没有返回值。
用法举例:
LPCRITCAL_SECTIONhCriticalSection;
CRITICAL_SECTIONCritical:
hCriticalSection=&Critical;
InitializeCriticalSection(hCriticalSection);
进入临界区
EnterCriticalSection()等待进入临界区的权限,当获得该权限后进入临界区。
原型:
VOIDEnterCriticalSection(
LPCRITICAL_SECTIONIpCriticalSection
);
参数说明:
IpCriticalSection:指出临界区对象的地址。
返回值:
该函数没有返回值。
用法举例:
LPCRITICAL_SECTIONhCriticalSection;
CRITICAL_SECTIONCritical;
hCriticalSection=&Critical;
EnterCriticalSection(hCriticalSection);
退出临界区
LeaveCriticalSection。释放临界区的使用权限。
原型:
VOIDLeaveCriticalSection(
LPCRITICAL_SECTIONIpCriticalSection
);
参数说明:
IpCriticalSection:指出临界区对象的地址。
返回值:
该函数没有返回值
用法举例:
LPCRITICAL_SECTIONhCriticalSection;
CRITICAL_SECTIONCritical;
hCriticalSection=&Critical;
LeaveCriticalSection(hCriticalSection);
删除临界区
DeleteCriticalSection()删除与临界区有关的所有系统资源。
原型:
VOIDDeleteCriticalSection(
LPCRITICAL_SECTIONIpCriticalSection
);
参数说明:
IpCriticalSection:指出临界区对象的地址。
返回值:
该函数没有返回值。
用法举例:
LPCRITICAL_SECTIONhCriticalSection;
CRITICAL_SECTIONCritical;
hCriticalSection=&Critical;
DeleteCriticalSection(hCriticalSection);
互斥对象
互斥对象(Mutex)包括创建互斥对象(CreateMutex())、打开互斥对象(OpenMutex
())及释放互斥对象(ReleaseMutex())API函数。
创建互斥对象
CreateMutex(0用于创建一•个互斥对象。
原型:
HANDLECreateMutex(
LPSECURITY_ATTRIBUTESIpMutexAttributes,
BOOLblnitialOwner,
LPCTSTRIpName
);
参数说明:
IpMutexAttributes:指定安全属性,为NULL时,信号量得到一个,默认的安全描述符。
blnitialOwner:指定初始的互斥对象。如果该值为TRUE并且互斥对象已经纯在,则调
用线程获得互斥对象的所有权,否则调用线程不能获得互斥对象的所有权。想要知道互斥对
象是否已经存在,参见返回值说明。
IpName:给出互斥对象的名字。
返回值:
互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的
互斥对象,将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调用函数
GetLastError()查询失败的原因。
用法举例:
staticHANDLEhHandlel=NULL;
〃常见一个名为"MutexNamel"的互斥对象
hHandlel=CreateMutex(NULL,FALSE,"MutexNamel");
打开互斥对象
OpenMutex()用于打开一个互斥对象。
原型:
HANDLEOpenMutex(
DWORDdwDesiredAccess,
BOOLblnheritHandle,
LPCTSTRIpName
);
参数说明:
指明系统安全属性支持的对互斥对象所有可能的访问。如果系统安全属性不支持,则不
能获得对互斥对象访问权。
(1)dwDesireAccess:指出发开后要对互.斥对象进行何种访问,具体描述如表2-4所示。
表2-4对互斥对象的访问种类
访问―
-MUTEX_ALL_ACCESS可以进行对任何互斥对象的访问
SYNCHRONIZE使用等待函数waitfunctions等待互斥对象成为可
用状态或使用
ReleaseMutex()释放使用权,从而获得互斥对象的
使用权
(2)blnheritHandle:指出返回信号量的句柄是否可以继承。
(3)IpName:给出信号量的名字。
返回值
互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调用
函数GetLastError()查询失败的原因。
用法举例:
staticHANDLEhHandlel=NULL;
〃打开一个名为"MutexNamel"的互斥对象
hHandle=OpenMutex(SYNCHRONIZE,NULL,"MutexNamel");
3.释放互斥对象
ReleaseMutex()用于释放互斥对象。
原型:
BOOLReleaseMutex(
HANDLEhMUTEX;
);
参数说明:
hMutex:Mutex对象的句柄。CreateMutex()和OpenMutex()函数返回I该句
柄。
返回值:
如果成功,将返回一个非0值;如果失败系统将返回0,可以调用函数GetLastErrorO查
询失败的原因。
用法举例:
staticHANDLEhHandlel=NULL;
BOOLrc;
rc=ReleaseMutex(hHandlel)
2.3.3实验内容
完成两个子线程之间的互斥。在主线程中使用系统调用CreleaseThread()创建两个子线
程,并使两个子线程互斥的使用全局变量count。
2.3.4实验要求
能正确的使用临界区对象,包括初始化临界区lnitializeCriticalSection()进入临界区
EnterCriticalSection()>退出临界区LeaveCritical()及删除临界区DeleteCriticalSection()进
一步理解线程的互斥。
2.3.5实验指导
具体操作过程同实验一,在MicrosoftVisualC++6.0环境下建立一个MFC支持的捽制
台工程文件,编写C程序,在主线程中使用Initia程eCriticalSection()初始化临界区,然后
建立两个子线程,在两个子线程中使用全局变量count的前、后分别使用EnterCriticalSeation
()进入临界区LeaveCriticalSection()退出临界区,等两个子线程运行完毕,主线程使用
DeleteCriticalSection()删除临界区并撤销线程。
2.3.6实验总结
该试验完成了两个子线程之间的互斥。若去掉互斥对象,观察全局变量count的变化,
了解互斥对象的作用,进一步理解线程的互斥。本试验也可以使用互斥对象(Mutex)来完
成两个线程的互斥,互斥对象(Mutex)的使用方法与信号量对象相似,这里不再说明,请
同学自己完成。线程互斥访问全局变量count
如图2-5所示。
2.3.7源程序
//Mutex.cpp:Definestheentrypointfortheconsoleapplication.
//
include"stdafx.h"
#include"Mutex.h"
#ifdef_DEBUG
#definenewDEBUG_NEW
#undefTHIS_FILE
staticcharTHIS_FILE[]=_FILE_;
#endif
llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll
〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃〃//Theoneandonlyapplicationobject
CWinApptheApp;
Usingnamespacestd;
staticintcount=5;
staticHANDLEhl;
staticHANDLEh2;
LPCRITICAL_SECTIONhCriticalSection;〃定义指向临界区对象的地址指针
CRITICAL_SECTIONCritical;〃定义临界区
voidfuncl();
voidfunc2();
lnt_tmain(intargc,TCHAR*argv[],TCHAR*envp[])
{intnRetCode=0;
DWORDdwThreadIDl,dwThreadlD2;
HCriticalSection=&Critical;〃将指向临界区对象的指针指向临界区
InitializeCriticalSection(hCriticalSection);〃初始化临界区
hl=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)funcl,
(LPVOID)NULL,
0,&dwThreadlDl);〃创建线程funcl
if(hl==NULL)printf("ThreadlcreateFAIL!\n,,);
elseprintf("ThreadlcreateSuccess!\nv);
h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func2,
(LPVOID)NULL,
0,&dwThreadlD2);〃创建线程func2
if(h2==NULL)printf("Thread?createFAIL!\n,,);
elseprintf("Thread?createSuccess!\n,,)
Sleep(lOOO);
CloseHandle(hl);
CloseHandle(h2);
DeleteCriticalSection(hCriticalSection);〃删除临界区
ExitThread(O);
ReturnnRetCode;
)
voidfunc2()
{intr2;
EnterCriticalSection(hCriticalSection);〃进入临界区
r2=count;
_sleep(100);
3r2+1;
count=r2;
printf(z/countinfunc2=%d\n?,,count);
LeaveCriticalSection(hCriticalSection);〃退出临界区
voidfuncl()
{intrl;
EnterCriticalSection(hCriticalSection);〃进入临界区
Rl=count;
_sleep(500);
rl=rl+l;
count=rl;
printf(/zcountinfuncl=%d\n,/,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()创建••个命名管道实例,并返回该管道的句柄。
原型:
HANDLECreateNamePipe(
LPCTSTRIpName,〃命名管道的名字
DWORDdwOpenMode,〃命名管道的访问模式
DWORDdwPipe'Mode,〃命名管道的模式
DWORDnMaxInstances,〃可创建实例的最大值
DWORDnOutBufferSize,〃以字节为单位的输出缓冲区的大小
DWORDnlnBufferSize,〃以字节为单位的输入缓冲区的大小
DWORDnDefaultTimeOut,〃默认超时时间
LPSECURITY_ATTRIBUTESIpSecurityAttributes〃安全属性
);
参数说明:
IpName:为命名管道的名字,管道的命名方式为\\ServerName\pipe\pipename,其中
ServerName为用命名管道通信时服务器的主机名或IP地址,pipename为命名管道的名字,
用户可自行定义。
dwOpenMode:指出命名管道的访问模式。模式如表2-5所示。
dwPipeMode:指出管道的模式。模式如表2-6所示、
nMaxInstances:该命名管道可以创建实例的最大值。
nOutBufferSize:输出缓冲区的大小,以字节为单位。
nlnBufferSize:输入缓冲区的大小,以字节为单位。
nDefaultTimeOut:默认的超时时间,以ms为单位。如果函数WaitNamePipe。指出
NMWAIT_USE_DEFAULT_WAIT,每个管道实例必须指定同一值的名字。
IpSecurityAttributes:为管道指定安全属性,为NULL时,管道得到一个默认的安全描述
符。
返回值:
如果管道创建成功,将返回服务器命名管道实例的句柄。如果失败,返回
INVALID_HANDLE_VALUE,可以调用函数GetLastError()查询失败的原因;当返回
ERROR_INVALID_PARAMETER时,表明参数nMaxInstances指定的值大于
PIPE_UNLIMTED_INSTANCES„
2.连接命名管道
服务器用函数ConnectNamePipe()连接命名管道。创建后命名管道也等待客户端的连接,
客户端可以使用函数CreateFile()和CallNamedPipe。进行连接。
原型:
BOOLConnectNamedPipe(
HANDLEhNamePipe,〃命名管道实例句柄
LPOVERLAPPEDIpOverlapped〃指向Overlapped结构的指针
);
参数说明:
hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。
IpOverlapped:指向Overlapped结构的指针,可设其为NULL。
返回值:
成功,将返回一个非0值;失败,系统返回0,可以调用函数GetLastError()查询失败的
原因。
3.拆除命名管道的连接
函数DisconnecrNamePipe()拆除命名管道服务器与客户端的连接。
原型:
BOOLDisconnectNamePipef
HANDLEhNamePipe
);
参数说明:
hNamedPipe:为命名管道创建时得到的一个命名管道实例句柄。
返回值:
成功,将返回一个非。值;失败,系统返回0,可以调用函数GerLasrError()查询失败的
原因。
4.客户端连接服务器已建立的命名管道
客户端使用函数CallNamePipe()连接服务器建立的命名管道。
原型:
BOOLCallNamePipe(
LPCTSTRIpNamePipeName,〃命名管道的名字
LPVOIDIpInBuffer;〃输出数据缓冲区
DWORDnlnBufferSize,〃以字节为单位的输出数据缓冲区的大小
LPVOIDIpOurBuffer,〃输入数据缓冲区指针
DWORDnOurBufferSize,〃以字节为单位的输入数据缓冲区的大小
LPDWORDIpBytesRead,〃输入字节数指针
DWORDnTimeOUT〃等待时间
);
参数说明:
IpNamedPipeName:命名管道的名字。
IpInBuffer:指出用于输出数据(向管道写数据)的缓冲区指针。
nlnBufferSize:用于输出数据缓冲区的大小,以字节为单位。
IpOutBuffer:指出用于接收数据(从管道读出数据)的缓冲区指针。
nOutBufferSize:指向用于接收数据缓冲区的大小,以字节为单位。
IpBytesRead:一个32位的变量,改变量用于存储从管道读出的字节数。
nTimeOut:等待命名管道成为可用状态的时间,单位为ms,
返回值:
成功,将返回一个非。值;失败,系统返回0,可用调用函数GetLastError。查询失败的
原因。
5.客户端等待命名管道
客户端使用函数WairNamedPipe()等待服务器连接命名管道。
原型:
BOOLWaitNamedPipe(
LPCTSTRIpNamedPipeName,〃要等待的命名管道名
DWORDnTimeOut〃等待时间
);
参数说明:
IpNamedPipeName:要等待的命名管道的名字。
nTimeOut:等待命名管道成为可用状态的时间,单位为ms.
返回值:
在等待时间内要连接的命名管道可以使用,将返回一个非。值。在等待时间内要连接的
命名管道不可以使用,系统返回0,可用调用GetLastError。查询失败的原因。
2.4.3使用内容
使用命名管道完成两个进程之间的通信。
2.4.4实验要求
使用Windows系统提供的命名管道完成两个进程之间的通信,要求能正确使用创建命名
管道CreateNamePipe()、连接命名管道ConnectNamePipe()、拆除命名道的连接
DisconnectNamePipe()、连接服务器已建立的命名管道CallNamePipe()、等待命名管道
WaitNamedPipe。等API.
2.4.5实验指导
完成两个进程之间的通信,需要建立两个过程文件,在MicrosoftVisualC++6.0环境下
建立服务器工程文件Pipeserver和客户端工程文件PipeClienr。在服务器程序中,首先使用
CreateNamePipe()建立一个命名管道,之后使
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 幼儿园教师职业认同感提升路径研究-基于幼儿园教师职业倦怠调查数据分析深度研究
- 管理信息系统
- 综合评标专家库水利工程专业评标专家考试题库及答案(2025年赣州)
- 图书馆项目绿色施工专项方案
- 煤矿企业事故隐患排查治理规定
- 环保工程质量检测方案
- (完整版)注浆加固施工方案
- 水利专业高级工程师职务任职资格评审量化评分表
- 2025-2030年毛刷加工机械行业跨境出海战略分析研究报告
- 猪浓缩饲料行业跨境出海战略分析报告
- 水厂供水安全培训资料课件
- 先进过程控制技术的实践与应用探讨
- 某铅锌矿开采设计毕业设计
- 校医基础知识培训课件
- 山东科技大学《概率论与数理统计》2024-2025学年第一学期期末试卷
- 性法医学图谱
- 废旧刀具管理办法
- 无讼学院实习律师培训结业考试题目含答案
- 土木工程水泥混凝土研究毕业论文范文
- DB65∕T 8006-2024 建筑吊篮安全施工管理规程
- 2025年四川省凉山州中考生物试卷真题(含答案解析)
评论
0/150
提交评论