生产者消费者问题
问题
一组生产者向一组消费者提供产品,它们共享一个有界缓冲区,生产者向其中投放产品,消费者从中取得产品。
规则
对于生产者:生产者生产一个产品(数据),正常放入仓库(缓冲区)。若有人正在访问仓库(缓冲区),则需要等待;若无则访问。若仓库(缓冲区)已满,则等待,等待一个消费者消费一个产品(数据),让仓库(缓冲区)不满,再唤醒;若不满则可直接放入。
对于消费者:消费者消费一个产品(数据),要去仓库(缓冲区)去取。若有人正在访问仓库(缓冲区),则等待。若仓库(缓冲区)为空,则等待一个生产者来生产产品(数据)供消费者消费;若不为空则取一个产品(数据)进行消费。
缓冲区是个临界资源,诸进程对缓冲区的操作程序是一个共享临界区,所以,还有个互斥的问题。
设置两个同步信号量及一互斥信号量
Empty:说明空缓冲单元的数目,其初值为有界缓冲区的大小n。即仓库有多少空位。
Full: 说明满缓冲单元的数目(即产品数目),其初值为0。即仓库已经有多少产品(有多少个位置被占)
Mutex: 说明该有界缓冲区是一临界资源,必须互斥使用,其初值为1,保证任何时候只有一个进程使用缓冲区。
Int in=0;out=0;
Item buffer[n];
Semaphore mutex=1,empty=n,full=0;
//生产者
void procedure() {
do{
produce an item in nextp;
…
wait(empty);//检验是否满了,若empty<0则阻塞 不执行下面的代码
wait(mutex);//表示正在使用缓冲区
buffer(in)=nextp;
in=(in+1)%n;
signal(mutex);//释放缓冲区
signal(full);//将产品数+1 做到进程同步
}while(TRUE);
}
//消费者
Void consumer(){
do{
wait(full);//检查是否为空,若full<0则说明缓冲区没有产品消费了 阻塞进程
wait(mutex);//表示正在使用缓冲区
nextc=buffer(out);
out=(out+1)%n;
signal(mutex);//释放缓冲区
signal(empty);//空位+1 做到进程同步
consumer the item in nextc;
…
}while (TRUE);
}
Void main() {
cobegin
procedure();
consumer();
coend;
}
注:
两个wait不可以互换位置,因为若互换,假设生产者进程执行,占用了临界资源mutex,而此时发现临界区满了(empty<0),便将自己阻塞而并未释放mutex;此时消费者资源过来,发现临界资源mutex(临界区)正在被人使用,便也阻塞,从而造成死锁。
两个signal同样不可以互换位置,同理。
哲学家进餐问题
问题
五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五碗通心粉和五根筷子,他们的生活方式是交替地进行思考和进餐。平时,哲学家进行思考,饥饿时便试图取用其左右两边最靠近他的筷子,只有在他拿到两根筷子时才能进餐。进餐后,放下筷子继续思考。
分析
两位哲学家不能抬起一根筷子吃饭,所以筷子是临界资源,一次只能在一个人的手里。
解决方法:用一个记录型信号量来控制一根筷子的互斥使用,由这5个信号量构成信号量数组。其描述如下:
var chopstick: array [0,…,4] of semaphore;
读者写者问题
问题
允许多个读进程同时读一个共享对象,因为读操作不会使数据文件混乱。但不允许一个Writer进程和其他Reader进程或Writer进程同时访问共享对象。因为这种访问将会引起混乱。
记录型信号量
为实现Reader和Writer进程间在读或写时的互斥而设置了一个互斥信号量Wmutex。另外,再设置一个整型变量readcount表示正在读的进程数目。
semaphore rmutex=1,wmutex=1;
int readcount=0;
void reader(){
do {
wait(rmutex);
if (readcount=0) wait(wmutex);
readcount++;
signal(rmutex);
…
perform read operation;
…
wait(rmutex);
readcount--;
if (readcount=0) signal(wmutex);
signal(rmutex);
}while(TRUE);
}
void writer() {
do {
wait(wmutex);
perform write operation;
signal(wmutex);
} while(TRUE);
}
Void main() {
cobegin
reader();writer();
coend;
}
只要有一个Reader进程在读,便不允许Writer进程去写。因此,仅当readcount=0,表示尚无Reader进程在读时,Reader进程才需要执行wait(wmutex)操作;若wait(wmutex)操作成功,Reader进程便可去读,相应地,做readcount+1操作。
同理,仅当Reader进程在执行了readcount减1操作后其值为0时,才须执行signal(wmutex)操作,以便让Writer进程写。
Readcount是一个可被多个Reader进程访问的临界资源,因此,应该为它设置一个互斥信号量rmutex。
信号量集
与前面的读者-写者问题不同,增加了一个限制,即最多只允许RN个读者同时读。为此,又引入了一个信号量L,并赋予其初值为RN,通过执行wait(L,1,1)操作,来控制读者的数目,每当有一个读者进入时,就要先执行wait(L,1,1)操作,使L的值减1。当有RN个读者进入读后,L便减为0,第RN+1个读者要进入读时,必然会因wait(L,1,1)操作失败而阻塞。
int RN;
semaphore L=RN,mx=1;
Void reader() {
do {
Swait(L,1,1);
//Swait(mx,1,0)语句起着开关的作用。只要无writer进程进入写,mx=1,reader进程就都可以进入读;一旦有writer进程进入写时,其mx=0,则任何reader进程就都无法进入读;
Swait(mx,1,0);
…
perform read operation;
…
Ssignal(L,1);
}while(TRUE);
}
void writer() {
do {
//Swait(mx,1,1,L,RN,0)语句表示仅当既无writer进程在写(mx=1),又无reader进程在读(L=RN)时,writer进程才能进入临界区写。
Swait(mx,1,1;L,RN,0);
perform write operation;
Ssignal(mx,1);
}while(TRUE);
}
Void main() {
cobegin
reader();writer();
coend;
}
总结了快一个下午…好累