题目2:用管程机制实现读者-写者问题

Download Report

Transcript 题目2:用管程机制实现读者-写者问题

互斥与同步
操作系统实验2
赵静:[email protected]
内容

一、信号量机制解决互斥与同步问题
实验:P/V信号量的编程实现

二、管程机制解决互斥与同步问题
实验:管程机制的编程实现
一、信号量机制解决互斥与同步问题
信号量机制
信号量(semaphore)机制
 解决并发进程同步的工具
 P操作表示同步进程发出的检测信号量操作,检测
是否能够使用临界资源
 V操作表示访问完临界资源的进程通知等待进程已
经完成了临界资源的访问,将临界资源释放。
经典互斥与同步问题回顾
 生产者-消费者问题
 读者-写者问题
 哲学家进餐问题
生产者-消费者问题

生产者P:
Wait(empty);
Wait(mutex);
Buffer(in)=nextp;
in:=(in+1) mod n;
Signal(mutex);
Signal(full);
mutex,full,empty:semaphore
mutex :=1;
full:=0;
empty:=n;

消费者C:
Wait(full);
Wait(mutex);
netxc=buffer(out);
out:=(out+1) mod n;
Signal(mutex);
Signal(empty);
读者-写者问题(读者优先)

读者R:
Wait(rmutex);
rcount++;
if(rcount == 1)
Wait(wmutex);
Signal(rmutex);
Read_Action();
Wait(rmutex);
rcount--;
if(rcount == 0)
Signal(wmutex);
Signal(rmutex);
rmutex,wmutex:semaphore
rmutex :=1;wmutex :=1;
rcount:int
rcount:=0;

写者W:
Wait(wmutex);
Write_Action();
Signal(wmutex);
哲学家进餐问题
 哲学家i(i=0,1..5)
array of fork[]:semaphore
fork[] :={1,1,1,1,1};
thinking();
Wait(fork[i]);
Wait(fork[(i+1) mod 5])
eating();
Signal(fork[i]);
Signal(fork[(i+1) mod 5])
思考,怎么解决
死锁问题 ?
同步对象
同步对象是指Windows/Linux中用于实现同步与互斥的实体,
包括互斥量(Mutex)、信号量(Semaphore)、临界区(Critical
Section)和事件(Events)等。
本实验中使用到信号量、互斥量和临界区三个同步对象。
实验方案设计
以读者-写者(读者优先)为例:
1.创建若干线程分别模拟读者操作和写者操作
2.读线程间和写线程间对各自局部共享资源的访问修改采用
Mutex对象,结合WaitForSingleObject 保证互斥操作。
3.读线程与写线程争用全局临界资源采用临界区(Critical
Seciton)。
4.统管读写线程的线程采用WaitForMultipleObjects 保证等待
所有的线程结束。
创建线程函数
CreateThread
功能:该函数创建一个在调用进程的地址空间中执行的线程。
若函数调用成功,返回值为新线程的句柄;若函数调用失败,
返回值为NULL。
格式:
HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter, DWORD dwCreationFlag,
LPDWORD lpThreadId);
参数说明:
lpThreadAttributes—指向一个LPSECURITY_ATTRIBUTES(新线程的安全性描述符
dwStackSize—定义原始堆栈大小。
lpStartAddress—指向使用LPTHRAED_START_ROUTINE类型定义的函数。
lpParamiter—定义一个给进程传递参数的指针。
dwCreationFlags—定义控制线程创建的附加标志。
lpThread—保存线程标志符(32位)
退出线程函数
ExitThread
功能:该函数结束一个线程
格式:VOID ExitThread (DWORD dwExitCode);
参数说明:
dwExitCode—调用线程的退出代码
创建互斥量函数
CreateMutex
功能:函数创建有名或者无名的互斥对象。如果函数调用成功,返回值
是互斥对象句柄;如果函数调用之前,有名互斥对象已存在,那么函数
给已存在的对象返回一个句柄。
格式:
HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner,
LPCTSTR lpName);
参数说明:
lpMutexAttributes—取值NULL。
bInitialOwner—指示当前线程是否马上拥有该互斥量(即马上加锁)。
lpName—互斥量名称。
释放互斥量函数
ReleaseMutex
功能:释放互斥对象的控制权。
格式:
BOOL WIANPI ReleaseMutex(HANDLE hMutex);
参数说明:
hMutex—互斥对象句柄。
返回值:TRUE表示成功,FALSE表示失败。
注:相当于我们的V操作
创建信号量函数
CreateSemaphore
功能:创建一个新的信号量。
格式:
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG
lInitialCount,
LONG
lMaximumCount,
LPCTSTR lpName);
参数说明:
lpSemaphoreAttributes——用于定义信号量的安全特性。
lInitialCount——设置信号量的初始计数。
lMaximumCount——设置信号量的最大计数。
lpName——指定信号量对象的名称。
释放信号量函数
ReleaseSemaphore
功能:函数用于对指定的信号量增加指定的值。
格式:BOOL ReleaseSemaphore(
HANDLE
hSemaphore,
LONG
lReleaseCount,
LPLONG
lpPreviousCount);
参数说明:
hSemaphore——所要释放的信号量句柄。
lReleaseCount——所要释放信号量的数目。
lpPreviousCount——指向返回信号量上次值的变量的指针,如果不需要信号量
上次的值,那么这个参数可以设置为NULL。
注:相当于我们的V操作
使用临界区资源的相关函数
VOID InltlallzeCriticalSection(
LPCRITICAL_SECTION IpCritiCalSection);
函数功能:该函数初始化临界区对象。返回值:无。
VOID EnterCriticalSection(
LPCRITICAL_SECTION lpCriticalSection);
函数功能:该函数是等待指定临界区对象的所有权。当调用线程被
赋予所有权时,该函数返回。返回值:无。
VOID LeaveCriticalSection(
LPCRITICAL_SECTION lpCriticalSection);
函数功能:该函数释放指定临界区对象的所有权。返回值:无。
等待同步对象函数
WaitForSingleObject
功能:使程序处于等待状态,直到信号量hHandle出现(即其值大于
等于1)或超过规定的等待时间。
格式:
DWORD WaitForSingleObject(HANDLE hHandle,
DWORD dwMilliseconds);
参数说明
hHandle——同步对象指针。
dwMilliseconds——等待的最长时间(INFINITE为无限等待)。
注:相当于我们的P操作
等待所有线程完成函数
waitForMultipleObjects
功能:WaiForMultipleObjects函数当下列条件之一满足时返回:
(1)任意一个或全部指定对象处于信号态;
(2)超时间隔已过。
格式:DWORD waitForMultipleObjects (DWORD nCount,
CONST HANDLE * lpHandles,
BOOL fWaitAll,
IDWORD dwMilliSeconds) ;
参数说明:
nCount——句柄的数量 最大值为MAXIMUM_WAIT_OBJECTS(64)
HANDLE——句柄数组的指针。
HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore )
数组
bWaitAll ——等待的类型,如果为TRUE 则等待所有信号量有效在往下执行,
FALSE 当有其中一个信号量有效时就向下执行。
dwMilliseconds——超时时间 超时后向执行。
如果为WSA_INFINITE 永不超时。如果没有信号量就会在这死等。
输入数据格式及运行效果
测试数据文件的例子:
1
2
3
4
5
R
W
R
R
W
3
4
5
6
5.1
5
5
2
5
3
提示:由于牵涉格式的问题,最好在文件中读取输入数据。
Shell脚本运行过程

编写shell脚本




使用任意编辑器(gedit、vim、nano等)编写脚本
添加执行权限: chmod u+x test.sh
运行脚本:
./test.sh
运行脚本的几种方式
 sh 脚本名:sh 只执行bash,读文件,只需r权限;开启新的儿子
shell运行脚本

./脚本名 :当前目录下该脚本,作为可执行程序运行,需x权限,每
次开启新的儿子shell运行脚本

. 脚本名:. 是一个命令,不开启儿子shell,当前shell运行
代码实例-初始化操作
代码实例-主线程
代码实例-主线程
代码实例-读者线程
代码实例-写者线程
思考
试把控制读/写的全局临界资源用Critical Section换
成互斥量mutex,看看实验结果有什么变化,并且查
阅相关资料说明原因。
实验内容
题目1:用PV信号量实现读者-写者问题(写者优先)程序。
题目2:用PV信号量实现生产者与消费者问题。
题目3:用PV信号量实现哲学家进餐问题,要求解决死锁问题。
题目4:江安北和江安南有一座小桥,小桥中间宽两边窄,中间
每次最多允许两人通过,两边每次最多允许一人通过,使用PV
信号量实现过此桥的程序。
实验要求
1.每人做一道题
2.学号末2位模4所得结果+1为该生所做题目号
实验:PV信号量解决互斥与同步问题
二、管程机制解决互斥与同步问题
管程的定义
管程的定义:
一组数据结构和数据结构上的一组相关操作被称为管
程。
 管程(monitor):新的同步机制
 用管程解决同步问题比用信号量解决同步问题更加
简单。
 管程机制作为同步工具,便于在高级语言编程中实
现。
管程的组成
管程由4部分组成:
管程的名称
 局部于管程内部的共享数据结构说明
对该数据结构进行操作的一组过程
 对局部于管程内部的共享数据设置初始的语句
管程的结构




管程只有一个入口和一个出口
并发进程在管程外等待调用管程
中的过程
如果进程在管程中不能访问临界
资源,则在管程中阻塞
等待访问完临界资源的进程将其
唤醒
利用管程实现进程同步的工具
在通过管程实现进程同步时,必须设置同步工具。
1.wait
2.Signal
3.condition
条件变量也是一种抽象的数据结构类型,对条件变量
的操作仅仅是wait和Signal。一个进程被阻塞或挂起
的原因可能有多个,因此在管程中可以设置多个条件
变量,形式为:
Var x, y:condition
对条件变量的操作:
x.wait x.signal
条件变量
条件变量是利用线程间共享的全局变量进行同步
的一种机制,主要包括两个动作:一个线程等待"条件
变量的条件成立"而挂起;另一个线程使"条件成立"
(给出条件成立信号)。为了防止竞争,条件变量的
使用总是和一个互斥锁结合在一起。
管程的关键的要素是:
1. 一个互斥量,让所有的线程互斥地访问管程保护的变量
2. 若干个条件变量,表示相应的条件,可用于阻塞线程
实验内容
题目1:用管程机制实现读者-写者问题(写者优先)程序。
题目2:用管程机制实现读者-写者问题,读者最大数目
限制为20(读者优先/写者优先均可)。
题目3:用管程机制量实现哲学家进餐问题,要求解决死锁问题。
题目4:江安北和江安南有一座小桥,小桥中间宽两边窄,中间
每次最多允许两人通过,两边每次最多允许一人通过,使用管
程机制实现过此桥的程序。
创建线程函数
pthread_create
功能:类Unix操作系统(Unix、Linux、Mac OS X等)
的创建线程的函数。
格式:int pthread_create(pthread_t *tidp,
pthread_attr *attr,
void *(*start_trn)(void *),
void *arg);
参数说明:
tidp——指向线程标识符的指针。
attr——设置线程属性。
start_trn——线程运行函数的起始地址。
arg——运行函数的参数。
返回值:若线程创建成功,则返回0。若线程创建失败,
则返回出错编号。
召回线程函数
pthread_join
功能:等待一个线程的结束。
格式:
int pthread_join(pthread_t thread, void **retval);
参数说明:
thread——线程标识符,即线程ID。
retval——用户定义的指针,用来存储被等待线程的返回值。
返回值:0代表成功。 失败,返回的则是错误号。
说明:pthread_join()函数,以阻塞的方式等待thread指定的
线程结束。当函数返回时,被等待线程的资源被收回。
如果进程已经结束,那么该函数会立即返回。
锁定互斥量函数
pthread_mutex_lock
功能:给互斥量加锁。
格式:
int pthread_mutex_lock(pthread_mutex_t *mutex);
参数说明:
mutex——互斥信号量指针。
返回值:在成功完成之后会返回零。其他任何返回值都表示
出现了错误。
说明:当pthread_mutex_lock()返回时,该互斥锁已被锁定。
线程调用该函数让互斥锁上锁,如果该互斥锁已被另一个线程
锁定和拥有,则调用该线程将阻塞,直到该互斥锁变为可用为
止。类似于我们的P操作。
释放互斥量函数
pthread_mutex_unlock
功能:给互斥量解锁。
格式:
int pthread_mutex_trylock(pthread_mutex_t *mutex);
参数说明:
mutex——需要解锁的互斥信号量指针。
返回值:在成功完成之后会返回零。其他任何返回值都表示
出现了错误。
说明:释放互斥锁,与pthread_mutex_lock成对存在。
类似于我们的V操作。
等待信号量函数
sem_wait
功能:可以获得资源,使semaphore的值减1。如果调用
sem_wait()时semaphore的值已经是0,则挂起等待。
格式:
int sem_wait(sem_t *sem);
参数说明:
sem——信号量资源指针。
返回值:在成功完成之后会返回零。其他任何返回值都表示
出现了错误。
说明:所需头文件:<semaphore.h>。
类似于我们的P操作。
释放信号量函数
sem_destroy
功能:释放资源,使semaphore的值加1,同时唤醒挂起等待
的线程。
格式:
int sem_destroy(sem_t * sem);
参数说明:
sem——信号量资源指针。
返回值:在成功完成之后会返回零。其他任何返回值都表示
出现了错误。
说明:所需头文件:<semaphore.h>。
类似于我们的P操作。
等待条件变量函数
pthread_cond_wait
功能:等待条件变量,若相应的条件变量已被占用则
阻塞线程等待,直到有其他线程释放该条件变量。
格式:
int pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex);
参数说明:
cond——条件变量指针。
mutex——互斥信号量指针。
返回值:成功则返回0,失败则返回出错编号。
释放条件变量函数
pthread_cond_signal
功能:释放条件变量,唤醒一个等待在指定条件变量
上的线程。
格式:int pthread_cond_signal(pthread_cond_t
*cond);
参数说明:
cond——将要释放的条件变量指针。
返回值:成功则返回0,失败则返回出错编号。
输入数据格式及运行效果
测试数据文件的例子:
1
2
3
4
5
R
W
R
R
W
3
4
5
6
5.1
5
5
2
5
3
提示:由于牵涉格式的问题,最好在文件中读取输入数据。
代码实例-初始化操作
代码实例-生产和消费产品
代码实例-生产者和消费者线程
代码实例-主线程
实验内容
题目1:用管程机制实现读者-写者问题(写者优先)程序。
题目2:用管程机制实现读者-写者问题,读者最大数目
限制为20(读者优先/写者优先均可)。
题目3:用管程机制量实现哲学家进餐问题,要求解决死锁问题。
题目4:江安北和江安南有一座小桥,小桥中间宽两边窄,中间
每次最多允许两人通过,两边每次最多允许一人通过,使用管
程机制实现过此桥的程序。
实验要求
1.每人做一道题
2.学号末2位*3模4所得结果+1为该生所做题目号
Shell脚本运行过程

编写shell脚本




使用任意编辑器(gedit、vim、nano等)编写脚本
添加执行权限: chmod u+x test.sh
运行脚本:
./test.sh
运行脚本的几种方式
 sh 脚本名:sh 只执行bash,读文件,只需r权限;开启新的儿子
shell运行脚本

./脚本名 :当前目录下该脚本,作为可执行程序运行,需x权限,每
次开启新的儿子shell运行脚本

. 脚本名:. 是一个命令,不开启儿子shell,当前shell运行
Linux下c编程



运行c文件,hello.c为例
gcc 文件名
g++ 文件名 -lpthread
./ 运行程序


gcc hello.c 编译+链接,得到可执行文件a.out
gcc –o hello hello.c 编译+链接,得到可执行文件
hello
C语言源程序
.c
编译器
gcc
可执行文件