实验二
一、实验目的
- 进一步掌握windows系统环境下线程的创建和撤销
- 熟悉windows系统提供的线程同步API(是WINDOWS提供给应用程序与操作系统的接口)
- 使用windows系统提供的线程同步API解决实际问题
二、实验准备
相关API函数
1.等待对象(wait fuctions)函数:
包括等待一个对象 WaitForSingleObject()和等待多对象 WaitForMultipleObject()两个API函数。
等待一个对象:
WaitForMultipleObject() //用于等待一个对象。
原型:
DWORD WaitForSingleObject(
HANDLE hHandle, //对象句柄
DWORD dwMilliseconds //等待时间
);
参数说明:
- hHandle:等待对象的对象句柄。该对象句柄必须为SYNCHRONIZE(同步)访问。
- dwMilliseconds:等待时间,单位为ms。若改值为0,函数在测试对象的状态后立即返回,若为INFINITE(无限的),函数一直等待下去,直到收到一个信号将其唤醒。
返回值:
如果返回成功,其返回值说明是何种事件导致函数返回。
等待的对象可以为以下九个对象之一:
- Change notification:变化通知
- Console input:控制台输入
- Events:事件
- Job:作业
- Mutex:互斥信号量
- Process:进程
- Semaphore:计数信号量
- Thread:线程
- Waitable timer:定时器
等待多个对象:
WaitForMultipleObject() //在指定时间内等待多个对象
原型:
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.信号量对象(semaphore):
包括创建信号量 CreateSemaphore()打开信号量 OpenSemaphore()及增加信号量的值 ReleaseSemaphore()API函数。
创建信号量:
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()查询失败的原因。
打开信号量:
OpenSemaphore() //用于打开一个信号量
原型:
HANDLE OpenSemaphore(
DWORD dwDesidedAccess, //访问标志
BOOL bInheritHandle, //继承标志
LPCTSTR lpNme //信号量名
);
参数说明:
- dwDesiredAccess:指出打开后要对信号量进行何种访问
- bInheritHandle:指出返回的的信号量句柄是否可以继承
- lpName:给出信号量的名字
返回值:
信号量打开成功,将返回信号量的句柄;如果失败,系统返回null,可以调用函数GetLastError()查询失败的原因。
增加信号量的值:
ReleaseSemaphore() //用于增加信号量的值
原型:
BOOL ReleaseSemaphore(
HANDLE hSemaphore, //信号量对象句柄
LONG lReleaseCount, //信号量要增加数值
LPLONG lpPreiousCount //信号量要增加数值地址
);
参数说明:
- hSemaphore:创建或打开信号量时给出的信号量对象句柄。Windows NT中建议使用SEMAPHORE_MODIFY_STARTE访问属性打开该信号量。
- lReleaseCount:信号量要增加数值。该值必须大于0。如果增加该值后大于信号创建时给出的lMaximumCount值,则增加操作失效,函数返回FALSE。
- lpPreiousCount :接收信号量的一个32位的一个变量。若不需要接受该值,可以指定为null。
返回值:
如果成功,将返回一个非0值;如果失败,系统返回一个0,可以调用一个GetLastError()查询失败的原因。
三、实验内容
(一)实验内容
完成主子两个线程之间的同步,要求子线程先执行。在主线程中使用系统调用CreateThread()创建一个子线程。主线程创建一个子线程后进入阻塞状态,直到子线程运行完毕后唤醒主线程。
(二)主要代码
// 002.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "002.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 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");//创建一个信号量,命名为SemaphoreName1
if (hHandle1==NULL)
printf("Semaphore Create Fail!\n");
else
printf("Semaphore Create Success!\n");
hHandle1=OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE,
NULL,
"SemaphoreName1"); //打开信号量SemaphoreName1
if (hHandle1==NULL)
printf("Semaphore Open Fail!\n");
else
printf("Semaphore Open Success!\n");
h1=CreateThread((LPSECURITY_ATTRIBUTES)NULL,
0,
(LPTHREAD_START_ROUTINE)func, //指明要调用的函数
(LPVOID)NULL,
0,&dwThreadID1); //创建子线程
if (h1==NULL) printf("Thread1 create Fail!\n");
else printf("Thread1 create Success!\n");
dRes=WaitForSingleObject(hHandle1,INFINITE); //主线程等待子线程结束
err=GetLastError();
printf("WaitForSingleObject err = %d\n",err);
if (dRes==WAIT_TIMEOUT)
printf("TIMEOUT!dRes=%d\n",dRes);
else if (dRes==WAIT_OBJECT_0)
printf("WAIT_OBJECT!dRes=%d\n",dRes);
else if (dRes==WAIT_ABANDONED)
printf("WAIT_ABANDONED!Dres=%d\n",dRes);
else
printf("dRes =%d\n",dRes);
CloseHandle(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=%d\n",err);
if(rc==0) printf("Semaphore Release Fail!\n");
else printf("Semaphore Release Success!rc= %d\n",rc);
}
四、实验结果与总结
输出结果:
总结:
1.本节实验学习了信号量的创建、打开、释放函数以及等待对象(wait fuctions)函数
但对于各个函数的参数掌握还不够熟练,需要多加练习。
2.主线程创建一个信号量,创建完后打开信号量,子线程执行相应的任务,主线程等待单个对象的完成;子线程任务执行完毕后释放信号量。
3.实验中调用了GetLastError()查询失败的原因,通过查询该函数的返回值即可找到失败原因。
4.等待对象(wait fuctions)函数中:WaitForMultipleObject(3,hHandles,1,INFINITE)
如果第三个参数设置为1/true,则需要等待数组中所有的对象完成,才可以继续执行主线上的任务;如果第三个参数设置为0/false,则只要数组当中满足一个任务的结束,就可以继续执行主线上的任务。
5.信号量函数用法举例:
创建信号量:
Static HANDLE hHandle1=null;
//创建一个信号量,其初值为0,最大值为5,信号量的名字为“SemphoreName1”
hHnadle1= CreateSemaphore(NULL,0,5,"SemphoreName1");
打开信号量:
Static HANDLE hHandle1=null;
//打开一个名为 ” SemphoreName1 ” 的信号量,之后可使用ReleaseSemaphore()函数增加信号量的值hHandle1=OpenSemaphore(SEMAPHORE_MOFDIFY_START,NULL, ” SemphoreName1” );
释放信号量:
Static HANDLE hHandle1=NULL;
BOOL rc;
rc= ReleaseSemaphore(hHandle1,1,NULL);//给信号量的值加1;
遇到的问题与解决方法:
①在VC++中输入汉字时无法显示输入框,可能是由于电脑自带的输入法与VC++不兼容的原因
解决方法:下载搜狗输入法
②仍是在实验一时遇到的问题(无法执行/运行错误),按照实验一 的解决方案即可。