【操作系统】实验二 线程的同步

本文详细介绍了在Windows系统环境下,如何使用线程同步API进行线程间的同步操作,包括信号量的创建、打开和释放,以及WaitForSingleObject函数的应用。通过一个具体的编程示例,演示了主子线程间如何通过信号量实现同步,确保子线程先于主线程执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实验二

一、实验目的

  • 进一步掌握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++不兼容的原因

解决方法:下载搜狗输入法

②仍是在实验一时遇到的问题(无法执行/运行错误),按照实验一 的解决方案即可。

实验目的 (1)掌握Windows系统提供的线程创建与撤销系统调用 (2)掌握Windows系统环境下线程的创建与撤销方法 2 实验准备知识 (1)线程创建 CreateThread()完成线程的创建。它在调用进程的地址空间上创建一个线程,执行指定的函数,并返回新建立线程的句柄。 原型: HANDLE CreateThread(   LPSECURITY_ATTRIBUTES lpThreadAttributes,   DWORD dwStackSize,   LPTHREAD_START_ROUTINE lpStartAddress,   LPVOID lpParameter,   DWORD dwCreationFlags,   LPDWORD lpThreadId);   参数说明:   lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL,表示使用缺省值。   dwStackSize,线程堆栈大小,一般=0,在任何情况下,Windows根据需要动态延长堆栈的大小。   lpStartAddress,指向线程函数的指针,形式:@函数名,函数名称没有限制,但是必须以下列形式声明:   DWORD WINAPI ThreadProc (LPVOID pParam) ,格式不正确将无法调用成功。   lpParameter:向线程函数传递的参数,是一个指向结构的指针,不需传递参数时,为NULL。   dwCreationFlags :线程标志,可取值如下   CREATE_SUSPENDED: 创建一个挂起的线程   0 :创建后立即激活。   lpThreadId:保存新线程的id。   返回值:   函数成功,返回线程句柄;函数失败返回false。 (2)撤销线程 ExitThread()用于撤销当前线程 原型: VOID ExitThread( DWORD dwExitCode ); 参数说明: DwExitCode:指定线程返回码 返回值: 该函数没有返回值 用法举例: ExitThread(0); (3)挂起线程 Sleep()用于挂起当前正在执行的线程 原型: VOID Sleep( DWORD dwMilliseconds ); 参数说明: dwMilliseconds:指定挂起时间,单位为ms(毫秒)。 返回值: 该函数没有返回值。 (4)关闭句柄 函数CloseHandle()用于关闭已打开对象的句柄,其作用与释放动态申请的内存空间类似,这样可以释放系统资源,使进程安全运行。 原型: BOOL CloseHandle( HANDLE hObject ); 参数说明: hObject:已打开对象的句柄。 返回值: 成功,返回值为非0值;失败,则返回值为0.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值