实验二:线程的同步
2.2.1 实验目的
进一步掌握windows系统环境下线程的创建和撤销
熟悉windows系统提供的线程同步API(是WINDOWS提供给应用程序与操作系统的接口)
使用windows系统提供的线程同步API解决实际问题
2.2.2 实验准备知识:相关API函数介绍
2.2.2.1等待对象 (一个)
等待对象(wait fuctions)函数包括等待一个对象 (WaitForSingleObject())和等待多对象 (WaitForMultipleObject())两个API函数。等待一个对象WaitForMultipleObject()用于等 待一个对象。他等待的对象可以为以下对象之一。
Change notification:变化通知。
Console input:控制台输入。
Events:事件。
Job:作业。
Mutex:互斥信号量。
Process:进程。
Semaphore:计数信号量。
Thread:线程。
Waitable timer:定时器。
原型:
DWORD WaitForSingleObject(
HANDLE hHandle, //对象句柄
DWORD dwMilliseconds //等待时间
);
参数说明:
hHandle:等待对象的对象句柄。该对象句柄必须为SYNCHRONIZE([ˈsɪŋkrənaɪz],同步)访问。
dwMilliseconds:等待时间,单位为ms。若改值为0,函数在测试对象的状态后立即返回,若为 INFINITE(无限的),函数一直等待下去,直到收到一个信号将其唤醒,如表2-1所示。
返回值:
如果返回成功,其返回值说明是何种事件导致函数返回。
函数描述:
访问 描述
WAIT_ABANDONED 等待对象的是一个互斥(mutex)对象,该互斥对象没有被拥有它的线程释放,他被设置为不能被唤醒
WAIT_OBJECT_0 指定对象被唤醒
WAIT_TIMEOUT 超时
用法举例:
Staitic HANDLE hHandle1=NULL;
DWORD dRes;
dRes=WaitForSingleObject(hHandle1, 10); //等待对象的句柄为hHandle,等待时间1000ms
2等待对象 (多个)
等待多个对象WaitForMultipleObject()在指定时间内等待多个对象,他等待的对象与 WaitForSingleObject()相同。
原型:
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,函数一直等待下去,直到收到一个信号将其唤醒。
返回值:
如果成功返回,其返回值说明是何种事件导致函数返回。
3 信号量对象
信号量对象(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()查询失败的原因。
用法举例:
Static HANDLE hHandle1=null;
//创建一个信号量,其初值为0,最大值为5,信号量的名字为“SemphoreName1”
hHnadle1= CreateSemaphore(NULL,0,5,"SemphoreName1");
打开信号量
OpenSemaphore()用于打开一个信号量。
原型:
HANDLE OpenSemaphore(
DWORD dwDesidedAccess, //访问标志
BOOL bInheritHandle, //继承标志
LPCTSTR lpNme //信号量名
);
参数说明:
(1).dwDesiredAccess:指出打开后要对信号量进行何种访问,如下所示。
访问 描述
SEMAPHORE_ALL_ACCESS 可以进行任何对信号量的访问
SEMPHORE_MODFIY_STATE 可以使用ReleaseSemaphore()修改信号量的值,使信号量的 值成为可用状态
SYNCHRONIZE 使用等待函数(wait functions),等待信号量成为可用状态
(2). bInheritHandle:指出返回的的信号量句柄是否可以继承。
(3).lpName:给出信号量的名字
返回值:
信号量打开成功,将返回信号量的句柄;如果失败,系统返回null,可以调用函数 GetLastError()查询失败的原因。;
用法举例:
Static HANDLE hHandle1=null;
//打开一个名为 ” SemphoreName1 ” 的信号量,之后可使用ReleaseSemaphore()函数增加 信号量的值hHandle1=OpenSemaphore(SEMAPHORE_MOFDIFY_START,NULL, ” SemphoreName1” );
3. 增加信号量的值
ReleaseSemaphore()用于增加信号量的值。
原型:
BOOL ReleaseSemaphore(
HANDLE hSemaphore, //信号量对象句柄
LONG lReleaseCount, //信号量要增加数值
LPLONG lpPreiousCount //信号量要增加数值地址
);
参数说明:
(1).hSemaphore:创建或打开信号量时给出的信号量对象句柄。Windows NT中建议使用 SEMAPHORE_MODIFY_STARTE访问属性打开该信号量。
(2). lReleaseCount:信号量要增加数值。该值必须大于0。如果增加该值后大于信号创建时 给出的lMaximumCount值,则增加操作失效,函数返回FALSE。
(3). lpPreiousCount :接收信号量的一个32位的一个变量。若不需要接受该值,可以指定 为null。
返回值:
如果成功,将返回一个非0值;如果失败,系统返回一个0,可以调用一个GetLastError() 查询失败的原因。
用法举例:
Static HANDLE hHandle1=NULL;
BOOL rc;
rc= ReleaseSemaphore(hHandle1,1,NULL);//给信号量的值加1;
2.2.3实验内容
完成主子两个线程之间的同步,要求子线程先执行。在主线程中使用系统调用 CreateThread()创建一个 子线程。主线程创建一个子线程后进入阻塞状态,直到子线程运行完毕后唤醒主线程。
2.2.4实验要求
能正确使用等待对象、WaitForSingleObject()或WaitForMultipleObject(0及信号量对象 CreateSemaphore()、OpenSemaphore()、ReleaseSemaphore()等系统调用,进一步理解线程的同步。
2.2.5实验指导
具体操作过程同本章实验一,在Microsoft visual C++6.0环境下建立一个MFC支持的控制台文件,编写C程序,在程序中使用CreateSemaphore(NULL,0,1,”SemaphoreName1”)创建一个名为“SemaphoreName1”的信号量,信号量的初始值为0,之后使用OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE,NULL, ”SemaphoreName1)打开该信号量,这里访问标志使用“SYNCHRONIZE|SEMAPHORE_MODIFY_STARTE”,以便之后可以使用WaitForSingleObject()等待该信号量及使用ReleaseSemaphore()释放该信号量,然后创建一个子线程,主线程创建子线程后调用WaitForSingleObject(hHandle1,INFINITE),这里等待时间设置为INFINITE表示一直等待下去,直到该信号量被唤醒为止。子线程结束,调用ReleaseSemaphore(hHandle1,1,NULL)释放信号量,使信号量的值加1。
2.2.6实验总结
实验完成了主、子线程的同步,主线程创建子线程后,主线程塞,让子线程先执行,等子线程执行完后,由子线程唤醒子线程。主子线程运行情况如图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWMKTzSc-1586510532837)(C:\Users\先\AppData\Roaming\Typora\typora-user-images\image-20200410170622581.png)]
2.2.7 源程序
// Semaphore.cpp : Defines the entry point for the console application.
//
#include “stdafx.h”
#include " Semaphore.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”);//创建一个信号量
if (hHandle1==NULL)printf(“Semaphore Create!\n”);
else printf(“Semaphore Create Success!\n”);
hHandle1=OpenSemaphore(SYNCHRONIZE|SEMAPHORE_MODIFY_STATE,
NULL,
“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);
}