一,实验目的
(1)熟练掌握Windows系统环境下线程的创建与撤销。
(2)熟悉Windows系统提供的线程互斥API。
(3)使⽤Windows系统提供的线程互斥API解决实际问题。
二,实验准备
相关API函数介绍
1 临界区对象
临界区对象(CriticalSection) 包括初始化临界区(InitializeCriticalSection())、进⼊临界区
( EnterCriticalSection ())、退出临界区( LeaveCriticalSection ())及删除临界区
(DeleteCriticalSection())等API函数。
1.1初始化临界区
InitializeCriticalSection():⽤于初始化临界区对象。
原型:
VOID InitializeCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
参数说明:
lpCriticalSection:指出临界区对象的地址。
返回值:该函数没有返回值。
1.2进⼊临界区
EnterCriticalSection():等待进⼊临界区的权限,当获得该权限后进⼊临界区。
原型:
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
参数说明:
lpCriticalSection:指出临界区对象的地址。
返回值:该函数没有返回值。
1.3退出临界区
LeaveCriticalSection():释放临界区的使⽤权限。
原型:
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
参数说明:
lpCriticalSection:指出临界区对象的地址。
返回值:该函数没有返回值。
1.4删除临界区
DeleteCriticalSection():删除与临界区有关的所有系统资源。
原型:
VOID DeleteCriticalSection(
LPCRITICAL_SECTION lpCriticalSection
);
参数说明:
lpCriticalSection:指出临界区对象的地址。
返回值:该函数没有返回值。
2 互斥对象
互斥对象(Mutex) 包括创建互斥对象(CreateMutex())、打开互斥对象(OpenMutex())及释
放互斥对象(ReleaseMutex())API函数。
2.1创建互斥对象
CreateMutex():⽤于创建⼀个互斥对象。
原型:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexASributes,
BOOL bInitialOwner,
LPCTSTR lpName
);
参数说明:
lpMutexASributes:指定安全属性,为NULL时,信号量得到⼀个,默认的安全描述符。
bInitialOwner:指定初始的互斥对象。如果该值为TRUE并且互斥对象已经纯在,则调⽤线程获得互斥对象的所有权,否则调⽤线程不能获得互斥对象的所有权。想要知道互斥对象是否已经存在,参⻅返回值说明。
lpName:给出互斥对象的名字。
返回值:
互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的互斥对象,将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调⽤函数GetLastError()查询失败的原因。
2.2打开互斥对象
OpenMutex():⽤于打开⼀个互斥对象。
原型:
HANDLE OpenMutex(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
LPCTSTR lpName
);
参数说明:
指明系统安全属性⽀持的对互斥对象所有可能的访问。如果系统安全属性不⽀持,则不能获得对互斥对象访问权。(1)dwDesireAccess:指出发开后要对互斥对象进⾏何种访问(2) bInheritHandle: 指出返回信号量的句柄是否可以继承。(3) IpName : 给出信号量的名字。
返回值
互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调⽤函数GetLastError()查询失败的原因。
2.3释放互斥对象
ReleaseMutex(): ⽤于释放互斥对象。
原型:
BOOL ReleaseMutex(
HANDLE hMUTEX;
);
参数说明:
hMutex:Mutex对象的句柄。CreateMutex()和OpenMutex()函数返回该句柄。
返回值:
如果成功,将返回⼀个⾮0值;如果失败系统将返回0,可以调⽤函数 GetLastError()查询失败的原因。
三,实验内容
(一)实验内容
完成两个⼦线程之间的互斥。在主线程中使⽤系统调⽤ CreateThread()创建两个⼦线程,并使两个⼦线程互斥的使⽤全局变量 count。
(二)主要代码
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); //创建线程 func1
if (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); //创建线程 func2
if (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=%d\n",count);
LeaveCriticalSection (hCriticalSection); //退出临界区
}
void func1()
{
int r1;
EnterCriticalSection (hCriticalSection); //进⼊临界区
r1=count;
_sleep(500);
r1=r1+1;
count=r1;
printf("count in func1=%d\n",count);
LeaveCriticalSection (hCriticalSection);//退出临界区
}
四,实验结果与总结
1,该试验完成了两个子线程之间的互斥。
2、进一步巩固和掌握第一二次实验线程的创建于撤销和线程的同步的一些基本函数的运用方法和各自的功能的基础上了解线程的互斥。
3,对初始化临界区(InitializeCriticalSection())、进⼊临界区( EnterCriticalSection ())、退出临界区( LeaveCriticalSection ())及删除临界区
(DeleteCriticalSection())等命令的使用方法以及他们各自的功能有所掌握。