经典的进程同步问题

生产者消费者问题

问题

一组生产者向一组消费者提供产品,它们共享一个有界缓冲区,生产者向其中投放产品,消费者从中取得产品。

规则

对于生产者:生产者生产一个产品(数据),正常放入仓库(缓冲区)。若有人正在访问仓库(缓冲区),则需要等待;若无则访问。若仓库(缓冲区)已满,则等待,等待一个消费者消费一个产品(数据),让仓库(缓冲区)不满,再唤醒;若不满则可直接放入。
在这里插入图片描述
对于消费者:消费者消费一个产品(数据),要去仓库(缓冲区)去取。若有人正在访问仓库(缓冲区),则等待。若仓库(缓冲区)为空,则等待一个生产者来生产产品(数据)供消费者消费;若不为空则取一个产品(数据)进行消费。
在这里插入图片描述
缓冲区是个临界资源,诸进程对缓冲区的操作程序是一个共享临界区,所以,还有个互斥的问题。

设置两个同步信号量及一互斥信号量
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;
  }

总结了快一个下午…好累
在这里插入图片描述

发布了40 篇原创文章 · 获赞 94 · 访问量 9574

猜你喜欢

转载自blog.csdn.net/qq_41718454/article/details/104076381
今日推荐