这一部分比较重要,理解的地方比较多,单独写一篇。
一、生产者消费者问题
一组生产者进程和一组消费者进程共享有限缓冲区,一个生产一个消费。
只有缓冲区有空时生产者才能生产放入。
只有缓冲区有产品的时候消费者才能消费。
而且缓冲区属于临界资源,同一时间只能一个进程操作。
举个简单的栗子:
先简单粗暴理解一下pv操作,p是等着,有值我才上(s>=0)。v操作是资源++。
semaphore mutex=1;//柜台钥匙(临界区互斥信号量)
semaphore empty=n;//柜台空位(空闲缓冲区)
semaphore full=0;//小熊饼干数量(缓冲区初始化为空)
producer(){
while(1){
produce an item in nextp;//生产一个小熊饼干
p(empty);//看一眼柜台,柜台有空位了我就放进去
p(mutex);//钥匙在这儿我就拿过来打开柜台
add nextp to buffer;//小熊饼干放进去
v(mutex);//关上柜台把钥匙放回去
v(full);//告诉别人小熊饼干多了一个
}
}
consumer(){
while(1){
p(full);//看一眼柜台,有小熊饼干
p(mutex);//看看钥匙在不在,在我就去拿了
remove an item from buffer;//拿走一个小熊饼干
v(mutex);//钥匙放回去
v(empty);//告诉老板柜台空位多了一个你赶紧去做
comsume the item;//吃掉这个小熊饼干
}
}
里面所有的先瞧上一眼,都对应着p操作的死等判断,while循环。
举个复杂的栗子:
semaphore plate=1,apple=0,orange=0;
dad(){
while(1){
prepare an apple;
p(plate);
put the apple on the plate;
v(apple);
}
}
mom(){
while(1){
prepare an orange;
p(plate);
put the orange on the plate;
v(orange);
}
}
son(){
while(1){
p(orange);
take an orange from the plate;
v(plate);
eat the orange;
}
}
son(){
while(1){
p(apple);
take an apple from the plate;
v(plate);
eat the apple;
}
}
其实和前一个栗子差不多,这里要判断一下是不是自己想要的,而且只有一个盘子,必须有人拿走水果释放盘子才能继续往里放入。
二、读者-写者问题
读者和写者两组并发进程,允许多个读者同时读,但是一旦有一个进程开始写,直到写完成之前都不允许其他进程读和写。
1.读者优先算法
int count=0;//记录当前的读者数量
semaphore mutex=1;//count变量的锁
semaphore rw=1;//读写区的锁
writer(){
while(1){
p(rw);//没有人在读和写的时候,上锁
writing;写
v(rw);//开锁
}
}
reader(){
while(1)
p(mutex);//没有人再修改count变量的时候,上锁
if(count==0) p(rw);//如果我是第一个读者,给区域上锁防止有进程来写
count++;读者+1
v(mutex);给count变量开锁
reading;读
p(mutex);给count上锁
count--;读者-1
if(count==0) v(rw);如果我是最后一个读者,给区域开锁
v(mutex);给count变量开锁
}
}
这样做的坏处是,只要有读进程活跃,写进程就会一直等待。如果陆续有读进程在读,则写进程可能因为等待时间太长而饿死。
2.写者优先算法
int cout=0;
semaphore mutex=0;
semaphore rw=1;
semaphore w=1;
writer(){
while(1){
p(w);//没人写的时候锁定
p(rw);//没人读的时候锁定
writing;
v(rw);
v(w);
}
}
reader(){
while(1){
p(w);没人要写的时候锁定
p(mutex);
if(count==0) p(rw);加锁防止进程写
count++;
v(mutex);
v(w);
reading;
p(mutex);
count--;
if(count==0) v(rw);
v(mutex);
}
}
这个相对于第一个算法的不同的是,第一个算法没有写申明,写者只有死等没有人读的时候才会进来写,读者读之前也不会询问有没有人将要写,反正此时没有人写我就进来读。所以写进程一旦没有区域的控制权就只能傻等所有的读进程都读完。这一个算法就在写进程前加了个声明,在我真正获得区域的控制权之前先告诉大家我想要写,每个读进程读之前也会看看有没有进程想写,有的话读进程就会等待,这时只要现在正在读的进程执行完毕,写进程就可以获得区域的控制权,新的读进程只能等待下一次区域被释放。
第二个算法其实不是完全写者优先,他叫读写公平算法,只是相对于算法一而言,写进程地位要高一点,可以不用傻等所有读进程。真正的写优先王道没讲,我也暂时不看了。
三、哲学家进餐问题
要拿起了两边的筷子才能进餐。
semaphore chopstick[5]={1,1,1,1,1};
pi(){
do{
p(chopstick[i]);
p(chopstick[(i+1)%5]);
eat;
v(chopstick[i]);
v(chopstick[(i+1)%5]);
think;
}while(1);
}
这个算法存在一个问题,如果大家同时拿起了左边的筷子,那么每一个人都陷入了死等状态。
正确的规则如下:
仅当一个哲学家左右两边的筷子都可用的时候才允许他去抓筷子。
semaphore chopstick[5]={1,1,1,1,1}
semaphore mutex=1;
pi(){
do{
p(mutex);//获得互斥量再取筷子,取就取走一了双再允许别人进行取操作
p(chopstick[i]);
p(chopstick[(i+1)%5]);
v(mutex);
eat;
v(chopstick[i]);
v(chopstick[(i+1)%5]);
thinck;
}while(1);
}
四、吸烟者问题
假设一个系统有三个抽烟者进程和一个供应者进程。
供应者供应三种材料:烟草,纸,和胶水。抽烟者只有拿到了这三种材料之后才可以吸烟。
一个抽烟者有烟草,一个有纸,另一个有胶水。供应者每次只将两种材料放到桌子上,拥有剩下那一种材料的抽烟者可以拿走材料并且抽掉它,并且给供应者一个信号告诉已经完成。然后供应者才会继续往上放另外两种材料。
int random;
semaphore leafAndPaper;
semaphore leafAndGlue;
semaphore paperAndGlue;
semaphore finish;
process producer(){
while(1){
random=任意整数;
random=random%3;
if(random==0) v(leafAndPaper);
else if(random==1) v(leafAndGlue);
else v(paperAndGlue);
放在桌子上;
p(finish);//等他们抽掉
}
}
process c1(){
while(1){
p(leafAndPaper);
卷烟抽掉;
v(finish);
}
}
process c2(){
while(1){
p(leafAndGlue);
卷烟抽掉;
v(finish);
}
}
process c3(){
while(1){
p(paperAndGlue);
卷烟抽掉;
v(finish);
}
}