(1)熟练掌握Windows系统环境下线程的创建与撤销。
(2)熟悉Windows系统提供的线程互斥API。
(3)使⽤Windows系统提供的线程互斥API解决实际问题。
临界区对象 CriticalSection 包括初始化临界区 InitializeCriticalSection() 、进⼊临界区 EnterCriticalSection () 、退出临界区 LeaveCriticalSection () 及删除临界区 DeleteCriticalSection() 等API函数。
原型:
VOID InitializeCriticalSection (
LPCRITICAL_SECTION lpCriticalSection
);
//lpCriticalSection:指出临界区对象的地址
原型:
VOID EnterCriticalSection (
LPCRITICAL_SECTION lpCriticalSection
);
//lpCriticalSection:指出临界区对象的地址
原型:
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection //指出临界区对象的地址
);
原型:
VOID DeleteCriticalSection (
LPCRITICAL_SECTION lpCriticalSection //指出临界区对象的地址
);
互斥对象 Mutex 包括创建互斥对象 CreateMutex() 、打开互斥对象 OpenMutex() 及释放互斥对象 ReleaseMutex() API函数。
原型:
HANDLE CreateMutex(
LPSECURITY_ATTRIBUTES lpMutexAttributes, //指定安全属性,为NULL时,信号量得到⼀个,默认的安全描述符
BOOL blnitialOwner, //指定初始的互斥对象。如果该值为TRUE并且互斥对象已经纯在,则调⽤线程获得互斥 对象的所有权,否则调⽤线程不能获得互斥对象的所有权。
LPCTSTR lpName //给出互斥对象的名字
);
返回值: 互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的互斥对象,
将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调⽤函数GetLastError()查询失败的原因
⽤法举例:
static HANDLE hHandle1=NULL;
//常⻅⼀个名为"MutexName1"的互斥对象
hHandle1=CreateMutex(NULL,FALSE, "MutexName1");
原型:
HANDLE OpenMutex(
DWORD dwDesiredAccess, //指出发开后要对互斥对象进行何种访问
BOOL blnheritHandle, //指出返回信号量的句柄是否可以继承
LPCTSTR lpName //给出信号量的名字
);
返回值:互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调⽤函数GetLastError() 查询失败的原因
⽤法举例:
static HANDLE hHandle1 = NULL;
hHandle=OpenMutex(SYNCHRONIZE,NULL,"MutexName1");//打开⼀个名为"MutexName1"的互斥对象
原型:
BOOL ReleaseMutex(
HANDLE hMUTEX //Mutex对象的句柄
);
返回值:如果成功,将返回⼀个⾮0值; 如果失败,系统将返回0,可以调⽤函数 GetLastError()查询失败的原因
能正确的使⽤临界区对象,包括初始化临界区 InitializeCriticalSection()、进⼊临界区 EnterCriticalSection()、退出临界区 LeaveCritical() 及删除临界区 DeleteCriticalSection() 进⼀步理解线程 的互斥。
// 003.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "003.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// The one and only application object
CWinApp 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);
if(h1==NULL)
printf("Thread1 create FAIL!\n");
else
printf("Thread1 create Success!\n");
h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL, //创建线程func2
0,
(LPTHREAD_START_ROUTINE)func2,
(LPVOID)NULL,
0,
&dwThreadID2);
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 func1(){
int r1;
EnterCriticalSection(hCriticalSection); //进入临界区
r1=count;
_sleep(500);
r1=r1+1;
count=r1;
printf("count in func1=%d\n",count);
LeaveCriticalSection(hCriticalSection); //退出临界区
}
void func2(){
int r2;
EnterCriticalSection(hCriticalSection); //进入临界区
r2=count;
_sleep(100);
r2=r2+1;
count=r2;
printf("count in func2=%d\n",count);
LeaveCriticalSection(hCriticalSection); //退出临界区
}
1.复习了直接、间接制约关系、临界资源的使用、访问临界资源的循环进程(Windows操作系统分区与理论分区有一定差别)学习了临界区相关API函数的相关知识,如临界区对象与互斥对象。
2.学习了临界区对象的使用,包括初始化临界区、进入区临界区、退出临界区、删除临界区。
3.通过具体的程序进一步理解了线程的互斥。此前了解最多的是PV操作的理论知识,通过本次实验熟悉了实际的使用。
1.申请进入临界区
2.放入产品
3.退出临界区,释放资源
1.申请进入临界区
2.取出产品
3.退出临界区,释放资源
此时二者共用临界资源,需要通过互斥信号量来申请临界区的使用权。
创建线程时变量很多,逗号与分号很容易写错,需要多加注意。