实验三:线程的互斥
2.3.1实验⽬的
(1)熟练掌握Windows系统环境下线程的创建与撤销。
(2)熟悉Windows系统提供的线程互斥API。
(3)使⽤Windows系统提供的线程互斥API解决实际问题。
2.3.2 实验准备知识:相关API函数介绍
2.3.2.1临界区对象
临界区对象(Cri2calSec2on)包括初始化临界区(Ini2alizeCri2calSec2on())、进⼊临界区
(EnterCri2calSec2on())、退出临界区(LeaveCri2calSec2on())及删除临界区
(DeleteCri2calSec2on())等API函数。
初始化临界区
Ini2alizeCri2calSec2on()⽤于初始化临界区对象。
原型:
VOID Ini2alizeCri2calSec2on(
LPCRITICAL_SECTION lpCri2calSec2on
);
参数说明:
lpCri2calSec2on:指出临界区对象的地址。
返回值:
该函数没有返回值。
⽤法举例:
LPCRITCAL_SECTION hCri2calSec2on;
CRITICAL_SECTION Cri2cal;
hCri2calSec2on=&Cri2cal;
Ini2alizeCri2calSec2on(hCri2calSec2on);
进⼊临界区
EnterCri2calSec2on()等待进⼊临界区的权限,当获得该权限后进⼊临界区。
原型:
VOID EnterCri2calSec2on(
LPCRITICAL_SECTION lpCri2calSec2on
);
参数说明:
lpCri2calSec2on:指出临界区对象的地址。
返回值:
该函数没有返回值。
⽤法举例:
LPCRITICAL_SECTION hCri2calSec2on;
CRITICAL_SECTION Cri2cal;
hCri2calSec2on=&Cri2cal;
EnterCri2calSec2on(hCri2calSec2on);
退出临界区
LeaveCri2calSec2on()释放临界区的使⽤权限。
原型:
VOID LeaveCri2calSec2on(
LPCRITICAL_SECTION lpCri2calSec2on
);
参数说明:
lpCri2calSec2on:指出临界区对象的地址。
返回值:
该函数没有返回值
⽤法举例:
LPCRITICAL_SECTION hCri2calSec2on;
CRITICAL_SECTION Cri2cal;
hCri2calSec2on=&Cri2cal;
LeaveCri2calSec2on(hCri2calSec2on);
删除临界区
DeleteCri2calSec2on()删除与临界区有关的所有系统资源。
原型:
VOID DeleteCri2calSec2on(
LPCRITICAL_SECTION lpCri2calSec2on
);
参数说明:
lpCri2calSec2on:指出临界区对象的地址。
返回值:
该函数没有返回值。
⽤法举例:
LPCRITICAL_SECTION hCri2calSec2on;
CRITICAL_SECTION Cri2cal;
hCri2calSec2on=&Cri2cal;
DeleteCri2calSec2on(hCri2calSec2on);
2.3.2.2互斥对象
互斥对象(Mutex)包括创建互斥对象(CreateMutex())、打开互斥对象(OpenMutex())及释
放互斥对象(ReleaseMutex())API函数。
创建互斥对象
CreateMutex(0⽤于创建⼀个互斥对象。
原型:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexASributes,
BOOL bIni2alOwner,
LPCTSTR lpName
);
参数说明:
lpMutexASributes:指定安全属性,为NULL时,信号量得到⼀个,默认的安全描述符。
bIni2alOwner:指定初始的互斥对象。如果该值为TRUE并且互斥对象已经纯在,则调⽤线程获得互斥
对象的所有权,否则调⽤线程不能获得互斥对象的所有权。想要知道互斥对象是否已经存在,参⻅返回
值说明。
lpName:给出互斥对象的名字。
返回值:
互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的互斥对象,
将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调⽤函数GetLastError()查询失败的
原因。
⽤法举例:
sta2c 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 func2ons 等待互斥对象成为可⽤状态或使⽤
ReleaseMutex()释放使⽤权,从⽽获得互斥对象的使⽤权
(2) bInheritHandle: 指出返回信号量的句柄是否可以继承。
(3) IpName : 给出信号量的名字。
返回值
互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调⽤函数
GetLastError()查询失败的原因。
⽤法举例:
sta2c HANDLE hHandle1 = NULL;
//打开⼀个名为"MutexName1"的互斥对象
hHandle=OpenMutex(SYNCHRONIZE,NULL,“MutexName1”);
3. 释放互斥对象
ReleaseMutex() ⽤于释放互斥对象。
原型:
BOOL ReleaseMutex(
HANDLE hMUTEX;
);
参数说明:
hMutex:Mutex对象的句柄。CreateMutex()和OpenMutex()函数返回该句柄。
返回值:
如果成功,将返回⼀个⾮0值;如果失败系统将返回0,可以调⽤函数 GetLastError()查询失败的原因。
⽤法举例:
sta2c HANDLE hHandle1=NULL;
BOOL rc;
rc= ReleaseMutex(hHandle1)
2.3.3 实验内容
完成两个⼦线程之间的互斥。在主线程中使⽤系统调⽤ CreateThread()创建两个⼦线程,并使两个⼦
线程互斥的使⽤全局变量 count。
2.3.4 实验要求
能正确的使⽤临界区对象,包括初始化临界区 Ini2alizeCri2calSec2on()、进⼊临界区
EnterCri2calSec2on()、退出临界区 LeaveCri2cal()及删除临界区 DeleteCri2calSec2on() 进⼀步理解线程
的互斥。
2.3.5 实验指导
具体操作过程同实验⼀,在 Microsoa Visual C++ 6.0 环境下建⽴⼀个MFC⽀持的控制台⼯程⽂件,编
写C程序,在主线程中使⽤Ini2alizeCri2calSec2on()初始化临界区,然后建⽴两个⼦线程,在两个⼦线程
中使⽤全局变量 count 的前、后分别使⽤EnterCri2calSea2on()进⼊临界区LeaveCri2calSec2on()退出临
界区,等两个⼦线程运⾏完毕,主线程使⽤DeleteCri2calSec2on()删除临界区并撤销线程。
2.3.6 实验总结
该试验完成了两个⼦线程之间的互斥。若去掉互斥对象,观察全局变量count的变化,了解互斥对象
的作⽤,进⼀步理解线程的互斥。本试验也可以使⽤互斥对象(Mutex)来完成两个线程的互斥,互斥对
象(Mutex)的使⽤⽅法与信号量对象相似,这⾥不再说明,请同学⾃⼰完成。线程互斥访问全局变量
count
如图 2-5所示。
2.3.7 源程序
//Mutex.cpp:Defines the entry point for the console applica2on.
//
#include “stdafx.h”
#include “Mutex.h”
#ifdef_DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
sta2c char THIS_FILE[ ]=FILE;
#endif
//
//The one and only applica2on object
CWinApp theApp;
Using namespace std;
sta2c int count =5;
sta2c HANDLE h1;
sta2c HANDLE h2;
LPCRITICAL_SECTION hCri2calSec2on; //定义指向临界区对象的地址指针
CRITICAL_SECTION Cri2cal; //定义临界区
void func1();
void func2();
Int _tmain(int argc,TCHARargv[],TCHARenvp[])
{int nRetCode=0;
DWORD dwThreadID1, dwThreadID2;
HCri2calSec2on=&Cri2cal; //将指向临界区对象的指针指向临界区
Ini2alizeCri2calSec2on(hCri2calSec2on); //初始化临界区
h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func1,
(LPVOID)NULL,
0,&dwThreadID1); //创建线程 func1
if (h1NULL) prinq(”Thread1 create FAIL!\n”);
else prinq(”Thread1 create Success!\n”);
h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func2,
(LPVOID)NULL,
0,&dwThreadID2); //创建线程 func2
if (h2NULL) prinq(”Thread2 create FAIL!\n”);
else prinq(”Thread2 create Success!\n”)
Sleep(1000);
CloseHandle(h1);
CloseHandle(h2);
DeleteCri2calSec2on(hCri2calSec2on); //删除临界区
ExitThread(0);
Return nRetCode;
}
void func2()
{ int r2;
EnterCri2calSec2on (hCri2calSec2on); //进⼊临界区
r2=count;
_sleep(100);
r2=r2+1;
count=r2;
prinq(“count in func2=%d\n”,count);
LeaveCri2calSec2on (hCri2calSec2on); //退出临界区
}
void func1()
{ int r1;
EnterCri2calSec2on (hCri2calSec2on); //进⼊临界区
R1=count;
_sleep(500);
r1=r1+1;
count=r1;
prinq(“count in func1=%d\n”,count);
LeaveCri2calSec2on (hCri2calSec2on); //退出临界区
2.3.8 实验展望
上⾯的实验是使⽤临界区对象(Cri2calSec2on)实现的,同学们可以使⽤互斥对象(Mutex)来完
成。
在完成以上三个实验后,同学们对Windows系统提供的线程创建与撤销、线程的同步与互斥、API有
了⼀定的了解,在此基础上设计并完成⼀个综合性的实验解决实际同步与互斥问题,如⽣产者与消费者
问题、读者与写者问题等。饰演的题⽬可以⾃⾏设计,但要求必须涉及线程的创建与撤销、等待对象函
数(waitFunc2ons)、信号量对象(Semaphore)临界区对象(Cri2calSec2on)或互斥对象(Mutex)的使用
OS实验三:线程的互斥
最新推荐文章于 2021-07-15 04:09:14 发布