经典PV操作问题详解最全面的PV资料Word文件下载.docx
《经典PV操作问题详解最全面的PV资料Word文件下载.docx》由会员分享,可在线阅读,更多相关《经典PV操作问题详解最全面的PV资料Word文件下载.docx(18页珍藏版)》请在冰豆网上搜索。
可能“无限等待”,即等待队列头的进程得不到释放。
(2)思路:
令每个信号量上的等待队列中始终只有一个进程。
解决方案如下:
(n个进程)
n个进程至多有n-1个等待。
设置n-1个信号量,每个进程阻塞在不同的信号量上,使每个等待队列至多有一个进程等待。
用循环模拟队列。
SemaphoreS[n-1];
//S[i]的初值为i+1
Procedure_i()
intj;
DO_PRE_JOB();
for(j=n-2;
j>
=0;
j--)
P(S[j]);
DO_JOB_IN_CRITICAL_SECTION();
for(j=0;
j<
=n-2;
j++)
V(S[j]);
……
二、经典进程同步问题
总述:
进程同步问题主要分为以下几类:
一(生产者-消费者问题);
二(读者写者问题);
三(哲学家就餐问题);
四(爱睡觉的理发师问题);
五(音乐爱好者问题);
六(船闸问题);
七(红黑客问题)等。
其中前两类都是用于处理进程之间通信的问题:
生产者-消费者问题主要实现进程的消息机制,而读者-写者问题用于实现管道通信。
哲学家就餐问题是经典的互斥转同步防止死锁的多资源争夺。
理发师问题适合I/O或外部设备的管理,如打印调度。
红黑客问题是解决不同条件触发事件的思想方法。
I.生产者—消费者问题(初始缓冲区为空)
问题:
生产者生产产品放到缓冲区,消费者从缓冲区取产品消费。
①单缓冲区[书P119](适合单或多生产消费者):
同步:
生产者不能往满缓冲区放产品(S1
(1));
消费者不能从空缓冲区取产品(S2(0))。
voidProducer()
while(true){
生产一个产品;
P(S1);
申请一个空的缓冲区
放到缓冲区;
V(S2);
返回一个满的缓冲区
}}
voidConsumer()
P(S2);
申请一个满的缓冲区
从缓冲区取一个产品;
V(S1);
返回一个空的缓冲区
消费产品;
②环行多缓冲区(或无穷缓冲区)单生产消费者[习题P173-13]:
生产者不能往满缓冲区放产品(S1(n));
n为缓冲区大小。
互斥:
设置指示下一个空缓冲区的位置变量(i(0))和指示下一个产品在缓冲区的位置变量(j(0)),由于只有一个生产者和消费者,i和j无须互斥访问。
此问题无互斥关系。
生产了一个产品;
P(S1);
把产品放入缓冲区;
i=(i+1)%n;
//无穷缓冲区无须’%n’
V(S2);
取一个产品;
j=(j+1)%n;
③环行多缓冲区多生产消费者[书P120]:
设置指示下一个空缓冲区的位置变量(i(0)),生产者之间互斥(mutex1
(1));
设置指示下一个产品在缓冲区的位置变量(j(0)),消费者之间互斥(mutex2
(1))。
也可以生产者和消费者之间都互斥(把mutex1和mutex2都换成一个mutex
(1))。
while(true){
P(mutex1);
一个生产者申请制造产品
指针移动到下一空的缓冲区
V(mutex1);
释放生产者
释放一个满的缓冲区
P(mutex2);
一个消费者申请消费
从第j个缓冲区取一个产品;
指向下一个满的缓冲区
V(mutex2);
释放消费者
释放一个空的缓冲区
④用进程通信(信箱通信)的方法解决上述问题[习题P175-27]:
msgbuffmb;
//messagebuffer
while
(1){
generatesth.tosend;
receive(consumer,&
mb);
//取一空缓冲区
create_message(&
//放产品到缓冲区
send(consumer,&
//生产好的产品发给消费者
//send和receive原语见信箱通信问题
{
//emptymessage
for(inti=0;
i<
n;
i++)
send(producer,&
//初始化,发n个空缓冲区给生产者
receive(producer,&
//收到一个产品
extractmessage;
//把产品保存下来
//把空缓冲区再发给生产者
dosth.;
//消费产品
⑤进程消息缓冲通信[书P128]:
发送进程把缓冲区中的消息挂到接收进程的消息链上。
发送进程发送消息数量不限,无消息时接收进程不能取信息,故设置当前消息数量(m-syn(0))。
发送和接收进程互斥访问消息队列首指针m-q,故设置互斥信号量(m-mutex
(1))。
空缓冲区个数为(s-b(n)),设置互斥访问信号量(b-mutex
(1))。
send(R,M)//把消息M发给R
找到接收进程R,否则错误返回;
申请缓冲区P(s-b);
P(b-mutex);
取一空缓冲区;
V(b-mutex);
把信息M复制到空缓冲区;
P(m-mutex);
把缓冲区挂到m-q上;
V(m-mutex);
V(m-syn);
receive(A)//把消息存到地址A
P(m-syn);
取一消息复制到A;
释放消息缓冲区;
⑥进程信箱通信[书P130,06年秋讲义]:
发送进程把信息发到信箱中,接收进程随时取信。
发送进程不能向满信箱中发信(full(0));
接收进程不能从空信箱中取信(empty
(1))。
send(N,M)//把信件M发到信箱N中
查找信箱N;
P(full);
把M送入信箱N;
V(empty);
receive(N,X)//从信箱N中取一封信放到X
P(empty);
从信箱N中取一封信放到X;
V(full);
⑦进程通信多发送接收者问题[习题P174-16]:
n1个进程通过m个缓冲区向n2个进程发送消息,每个消息所有接收进程都接收一次。
发送者不能向满缓冲区发信息(mutex_send[m]
(1));
接收者不能从空缓冲区接收信息(mutex_receive[m](0))。
设置指示下一个空缓冲区变量(cur(0)),发送进程互斥访问(mutex_cur
(1));
设置buffer_count[m](0)记录某个缓冲区被读过几次,若某个缓冲区被读过n2次,则可以释放,接收者互斥访问buffer_count(mutex_count[m]
(1))。
阻塞分析:
若接收者试图接收空缓冲区被阻塞在mutex_receive[k]上,则其他要访问同一缓冲区的接收者被堵塞在mutex_count[k]上;
若此时发送者向缓冲区k写入信息,则由第一个接收者释放其他接收者。
若有一发送者被阻塞在mutex_send[cur]上,则其他发送者被阻塞在mutex_cur上。
voidsend()
P(mutex_cur);
cur=(cur+1)%m;
//若阻塞则表示cur满
P(mutex_send[cur]);
写入buffer[cur];
//cur内容等待被读取
V(mutex_receive[cur]);
V(mutex_cur);
voidreceive()
While(true){
for(inti=0;
m;
i++){
P(mutex_count[i]);
buffer_count[i]++;
if(buffer_count[i]==1)//第一次读,有信息吗?
P(mutex_receive[i]);
//有信息(或已经有人在访问了),释放其他接收者
V(mutex_count[i]);
从buffer[i]中读;
if(buffer_count[i]==n2){//大家都收过一次了
buffer_count[i]=0;
//buffer_count恢复初值
V(mutex_send[i]);
//释放此缓冲区
II.读者—写者问题
读者—写者问题与生产者—消费者问题最大区别在于前者不存在同步问题,就是说不考虑读者没有东西读的问题,没有可读的直接“走人”。
而后者如果没有东西消费,就会阻塞等待。
第一类(读者优先)[书P121]:
写者在写,则其余写者和读者等待;
有读者在读,则其他读者可读,直到没有读者写者才能写。
写者之间互斥以及所有读者和写者之间互斥(write(0/1));
读者之间不互斥;
readcount记录当前读者个数(readcount(0)),多个读者对readcount互斥访问,访问后加上1(mutex
(1));
readcount是一个可被多个读者进程访问的临界资源,所以需要设置一个互斥信号mutex
当有读者在读时,所有写者堵塞在write上;
有写者在写时,第一个读者阻塞在write上,其余的阻塞在mutex上,所有写者阻塞在write上。
voidReader()
P(mutex);
一开始,读者申请一个位置去看书
readcount++;
然后逐渐有更多的读者读书,自动加上1
if(readcount==1)//第一个读者,可能有写者在写
P(write);
为写者申请一个空间写东西
V(mutex);
读者要释放空间或缓冲区了
为读者申请能读书的资源
readcount--;
读者读不到足够的书读者逐渐减少
if(readcount==0)//没有读者了,释放写者
V(write);
释放读者,让作者写书
voidWriter()
P(write);
写;
V(write);
//释放的可能是读者或写者
/*相对读者优先,即写者可以释放写者;
也可改为绝对读者优先,即只要有读者在,写者写完优先释放读者。
方法类似于写者优先。
*/
第二类(写者优先)[习题P175-24]:
写者写完,优先释放下一个写者;
读者在读,若无写者等待,则其他读者可读;
读者在读,若有写者等待,则其他读者等待。
写者之间互斥(write
(1)),读者和写者之间互斥(read
(1));
记录当前读者个数(readcount(0))和写者个数(writecount(0)),读者对readcount互斥访问(mutex1
(1)),写者对writecount互斥访问(mutex2
(1));
为保证在有读者等待的时候,新写者到来也优先释放写者,让等待的读者继续等待,不能将读者和写者阻塞在同一队列上(mutex3
(1))。
当有写者在写时,其余写者阻塞在write上,第一个读者阻塞在read上,其他的读者堵塞在mutex3上。
当一个写者写完后,若有写者等待,则唤醒一个写者,否则唤醒第一个读者,由第一个读者唤醒其余读者。
当有读者在读时,一读者R通过P(read)还未V(read),此时新到来的第一位写者W阻塞在read上,其余写者阻塞在mutex2上;
新到来的第一位读者R1阻塞在read上,其余读者阻塞在mutex3上:
(1)若正在等待的第一位写者W先于读者R1到来,则他先被读者R释放,接着W会释放其余写者,使原阻塞在mutex2上的写者通过mutex2,重新阻塞在write上,而所有等待的读者仍然阻塞在原来的地方;
(2)否则读者R先释放读者R1,R1代替R的位置,原来等待在mutex3上的第一位读者R2被阻塞在read上,但此时R2在read等待队列上必处于写者W之后,成为情况
(1)。
因此可以看到,在下面的方案中,新写者到来后,最多再放行一位读者。
P(mutex3);
//若去掉则读者和写者先来后到
P(read);
P(mutex1);
//其实有了read无需mutex1
if(readcount==1)
V(mutex1);
V(read);
V(mutex3);
reading;
if(readcount==0)
P(mutex2);
writecount++;
if(writecount==1)
V(mutex2);
writing;
writecount--;
if(writecount==0)
III.哲学家就餐问题
[书P122,06年秋讲义]
n个哲学家,n支筷子,仅当一个哲学家两边的筷子都可用时才可以拿筷子。
设置n个哲学家的状态变量(state[n](thinking/hungry/eating),初始值全thinking),用于判断一个哲学家的左右筷子是否能用;
设置n个信号量(s[n],初始全0),用于判断哲学家是否能吃饭。
每个哲学家对state和s互斥访问(mutex
(1))。
voidtest()
if(state[i]==hungry&
&
state[(i-1)%n]!
=eating&
state[(i+1)%n]!
=eating)
{//能吃饭,后面的P(s[i])不会阻塞
state[i]=eating;
V(s[i]);
voidPhilosopher(inti)
Thinking;
state[i]=hungry;
test(i);
//能吃饭吗?
P(s[i]);
//能吃饭!
其对应的V操作在test里
拿起左右筷子吃饭;
吃完放下左右筷子;
state[i]=thinking;
//吃完了,帮忙看看左右的能吃饭吗
test((i-1)%n);
test((i+1)%n);
IV.爱睡觉的理发师问题
单理发师[习题P174-18,01、06年试题,06年秋讲义]
理发师只在理发椅前,要么理发要么睡觉;
理发师睡觉时,第一个顾客唤醒理发师;
理发师在理发时,其余顾客在n张椅子上等待;
椅子不够时,顾客离开。
没有顾客时理发师不能理发,只能睡觉(customer(0));
理发师睡觉的时候顾客不能理发,需先唤醒(barber
(1))。
设置变量wait_count(0)来记录当前等待的顾客数,理发师和顾客以及顾客之间对其互斥访问(mutex
(1))。
voidBarber()
P(customer);
//有顾客吗?
没有就睡觉
P(mutex);
//来顾客了!
//进私室理发,把椅子让给其他人
wait_count--;
V(mutex);
cuthair;
//理完了一个,可以理下一个了
V(barber);
}//注意:
如果barber初值是0,则
}// V(barber)在cuthair之前
voidCostumer()
//防止两顾客同坐最后一个椅子
if(wait_count<
n){
wait_count++;
//有椅子做,不走了
V(costumer);
//我要理发!
叫醒理发师
P(barber);
//理发师忙吗?
忙就等一会
gethaircut;
}
else//没有椅子了,不剪了
银行叫号系统或多理发师问题[习题P174-20,2000年试题]
银行有n个柜台,每个顾客进入银行取号,若没有空闲柜台则等待,只要有柜台的人员空闲,就叫号。
没有顾客时,柜台人员等待。
没有顾客时柜台等待(client(0)),无空闲柜台时顾客持号等待(clerk(n))。
设置各柜台状态变量(state[n](available/unavailable),初始值全available),任何顾客或柜员均对其互斥访问(mutex
(1))。
voidClerk(inti)
P(client);
办理业务;
//办理完了,恢复状态
state[i]=available;
V(clerk);
//叫下一位顾客
}
voidClient()
P(clerk);
//有空柜台吗?
//找一个空闲柜台
for(intk=1;
k<
=n;
++k){
if(state[k]==available){
state[k]=unavailable;
break;
}//if
}//for
V(client);
//我来啦!
我要取钱!
在第k个柜台办理业务;
V.音乐爱好者问题
[习题P174-19,99年试题,06年秋讲义]
三个musiclover各有walkman、CD、battery,三样齐全才能听音乐;
老板一次只借出三样中的任何两样,收回后才再次借出。
没有musiclover老板不能借出东西(musiclover(0));
没有东西musiclover不能听音乐,设置no_wc(0)、no_cb(0)、no_bw(0)表示没有三样东西的其中两样。
voidboss()
While(true){
intprov=genprovide();
//借出某两种
if(prov==1)
V(no_wc);
elseif(prov==2)
V(no_cb);
else
V(no_bw);
P(musiclover);
//有人来借prov吗/还了才能借
voidmusiclover(loveri)
if(i==battery){
P(no_wc);
拿到东西,听音乐;
elseif(i==walkman){
P(no_cb);
else{
P(no_bw);
V(music_lover);
//听完了,还给老板
VI.船闸问题
[习题P174-20,98、03年试题,06年秋讲义]
A、B两地船只单向通过T级船闸的问题。
设置记录A到B和B到A的方向船只个数(A2B_count(0),B2A_count(0)),两个方向的船只互斥访问(mutexA
(1),mutexB
(1))。
再设置船闸信号量(lock
(1)),用于开关船闸。
voidshipA()
P(mutexA);
A2B_count++;
if(A2B_count==1)
P(lock);
V(mutexA);
通过船闸;
A2B_count--;
if(A2B_count==0)
V(lock);
shipB和shipA类似,将所有A、B互换就行
这是北大习题课答案。
双向通航问题限制条件多而易变,此处不再讨论。
若限制T级船闸只能过T只船,则需增加一个信号量(max_count_mutex(T))。
P(max_count_mutex);
V(max_count_mutex);
VII.红黑客问题
[2004年试题]
船每次坐