操作系统 实验三:线程的互斥

实验三:线程的互斥

2.3.1实验⽬的

(1)熟练掌握Windows系统环境下线程的创建与撤销。

(2)熟悉Windows系统提供的线程互斥API。

(3)使⽤Windows系统提供的线程互斥API解决实际问题。

2.3.2 实验准备知识:相关API函数介绍

2.3.2.1临界区对象

临界区对象(CriticalSection)包括初始化临界区(InitializeCriticalSection())、进⼊临界区

( EnterCriticalSection ())、退出临界区( LeaveCriticalSection ())及删除临界区

(DeleteCriticalSection())等API函数。

初始化临界区

InitializeCriticalSection()⽤于初始化临界区对象。

原型:

VOID InitializeCriticalSection(

LPCRITICAL_SECTION lpCriticalSection

);

参数说明:

lpCriticalSection:指出临界区对象的地址。

返回值:

该函数没有返回值。

⽤法举例:

LPCRITCAL_SECTION hCriticalSection;

CRITICAL_SECTION Critical;

hCriticalSection=&Critical;

InitializeCriticalSection(hCriticalSection);

进⼊临界区

EnterCriticalSection()等待进⼊临界区的权限,当获得该权限后进⼊临界区。

原型:

VOID EnterCriticalSection(

LPCRITICAL_SECTION lpCriticalSection

);

参数说明:

lpCriticalSection:指出临界区对象的地址。

返回值:

该函数没有返回值。

⽤法举例:

LPCRITICAL_SECTION hCriticalSection;

CRITICAL_SECTION Critical;

hCriticalSection=&Critical;

EnterCriticalSection(hCriticalSection);

退出临界区

LeaveCriticalSection()释放临界区的使⽤权限。

原型:

VOID LeaveCriticalSection(

LPCRITICAL_SECTION lpCriticalSection );

参数说明:

lpCriticalSection:指出临界区对象的地址。

返回值:

该函数没有返回值

⽤法举例:

LPCRITICAL_SECTION hCriticalSection;

CRITICAL_SECTION Critical;

hCriticalSection=&Critical;

LeaveCriticalSection(hCriticalSection);

删除临界区

DeleteCriticalSection()删除与临界区有关的所有系统资源。

原型:

VOID DeleteCriticalSection(

LPCRITICAL_SECTION lpCriticalSection

);

参数说明:

lpCriticalSection:指出临界区对象的地址。

返回值:

该函数没有返回值。

⽤法举例:

LPCRITICAL_SECTION hCriticalSection;

CRITICAL_SECTION Critical;

hCriticalSection=&Critical;

DeleteCriticalSection(hCriticalSection);

2.3.2.2互斥对象

互斥对象(Mutex)包括创建互斥对象(CreateMutex())、打开互斥对象(OpenMutex())及释

放互斥对象(ReleaseMutex())API函数。

创建互斥对象

CreateMutex(0⽤于创建⼀个互斥对象。

原型:

HANDLE CreateMutex(

LPSECURITY_ATTRIBUTES lpMutexASributes,

BOOL bInitialOwner,

LPCTSTR lpName

);

参数说明:

lpMutexASributes:指定安全属性,为NULL时,信号量得到⼀个,默认的安全描述符。

bInitialOwner:指定初始的互斥对象。如果该值为TRUE并且互斥对象已经纯在,则调⽤线程获得互斥

对象的所有权,否则调⽤线程不能获得互斥对象的所有权。想要知道互斥对象是否已经存在,参⻅返回

值说明。

lpName:给出互斥对象的名字。

返回值:

互斥对象创建成功,将返回该互斥对象的句柄。如果给出的互斥对象是系统已经存在的互斥对象,将返回这个已存在互斥对象的句柄。如果失败,系统返回NULL,可以调⽤函数GetLastError()查询失败的

原因。

⽤法举例:

static 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 functions 等待互斥对象成为可⽤状态或使⽤

ReleaseMutex()释放使⽤权,从⽽获得互斥对象的使⽤权

(2) bInheritHandle: 指出返回信号量的句柄是否可以继承。

(3) IpName : 给出信号量的名字。

返回值

互斥对象打开成功,将返回该互斥对象的句柄;如果失败,系统返回NULL,可以调⽤函数

GetLastError()查询失败的原因。

⽤法举例:

static HANDLE hHandle1 = NULL;

//打开⼀个名为"MutexName1"的互斥对象

hHandle=OpenMutex(SYNCHRONIZE,NULL,“MutexName1”);

  1. 释放互斥对象

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 源程序

#include "stdafx.h"
#include <Windows.h>


#ifdef _DEBUG 

#define new DEBUG_NEW 

#undef THIS_FILE 

static char THIS_FILE[ ]=__FILE__; 

#endif 

//

//The one and only applica2on 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); //创建线程 func1 

if (h1==NULL) printf("Thread1 create FAIL!\n"); 

else printf("Thread1 create Success!\n"); 

h2=CreateThread((LPSECURITY_ATTRIBUTES)NULL, 

0, 

(LPTHREAD_START_ROUTINE)func2, 

(LPVOID)NULL, 

0,&dwThreadID2); //创建线程 func2 

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 func2() 

{ int r2; 

 EnterCriticalSection (hCriticalSection); //进?临界区

 r2=count; 

 _sleep(100); 

r2=r2+1; 

count=r2; 

printf("count in func2=%d\n",count); 

LeaveCriticalSection (hCriticalSection); //退出临界区

} 

void func1() 

{ int r1; 

 EnterCriticalSection (hCriticalSection); //进?临界区

 r1=count; 

 _sleep(500); 

r1=r1+1; 

count=r1; 

printf("count in func1=%d\n",count); 

LeaveCriticalSection (hCriticalSection); //退出临界区
}

2.3.8 实验展望

上⾯的实验是使⽤临界区对象(Cri2calSec2on)实现的,同学们可以使⽤互斥对象(Mutex)来完

成。

在完成以上三个实验后,同学们对Windows系统提供的线程创建与撤销、线程的同步与互斥、API有

了⼀定的了解,在此基础上设计并完成⼀个综合性的实验解决实际同步与互斥问题,如⽣产者与消费者问题、读者与写者问题等。饰演的题⽬可以⾃⾏设计,但要求必须涉及线程的创建与撤销、等待对象函

数(waitFunc2ons)、信号量对象(Semaphore)临界区对象(Cri2calSec2on)或互斥对象(Mutex)的使

⽤。

在 C 语言中,我们可以使用 `pthread` 模块来创建和管理线程以及实现互斥访问。为了实现两个子线程之间的互斥,可以使用 `pthread_mutex_t` 结构体代表互斥锁。下面是一个简单的示例,展示了如何创建两个子线程互斥地增加全局变量 count: ```c #include <stdio.h> #include <pthread.h> // 定义全局变量和互斥锁 int count = 0; pthread_mutex_t mutex; void* thread_func_1(void* arg) { for (int i = 0; i < 10; ++i) { pthread_mutex_lock(&mutex); // 获取锁 printf("Thread 1: Count is %d\n", count++); pthread_mutex_unlock(&mutex); // 释放锁 } return NULL; } void* thread_func_2(void* arg) { for (int i = 0; i < 10; ++i) { pthread_mutex_lock(&mutex); // 获取锁 printf("Thread 2: Count is %d\n", count++); pthread_mutex_unlock(&mutex); // 释放锁 } return NULL; } int main() { // 初始化互斥锁 pthread_mutex_init(&mutex, NULL); // 创建两个线程 pthread_t t1, t2; int rc1 = pthread_create(&t1, NULL, thread_func_1, NULL); if (rc1 != 0) { perror("Error creating thread 1"); exit(1); } int rc2 = pthread_create(&t2, NULL, thread_func_2, NULL); if (rc2 != 0) { perror("Error creating thread 2"); exit(1); } // 等待两个线程结束 pthread_join(t1, NULL); pthread_join(t2, NULL); // 清理资源 pthread_mutex_destroy(&mutex); printf("Final count: %d\n", count); return 0; } ``` 在这个例子中,`thread_func_1` 和 `thread_func_2` 都会尝试获取互斥锁,当其中一个线程获得锁后,其他线程将被阻塞直到锁被释放。这样就实现了两个子线程对全局变量 `count` 的互斥访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值