生产者消费者问题、读者写者问题、哲学家问题细致讲解

1、生产者消费者问题

①系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品并使用。【注:这里的“产品”理解为某种数据】
生产者、消费者共享一个初始为空、大小为n的缓冲区。
只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待
【当缓冲区满时,生产者需要等待消费者取走产品才能继续操作,有顺序,所以是同步关系
只有缓冲区不空时,消费者才能从中取出产品,否则必须等待【同理也是同步关系】
缓冲区是临界资源,各进程必须互斥地访问

PV操作题目分析步骤:

  1. 关系分析:找出题目中描述的各个进程,分析它们之间的同步、互斥关系。
  2. 整理思路:根据各进程的操作流程确定P、V操作的大致顺序。
  3. 设置信号量:设置需要的信号量,并根据题目条件确定信号量初值。【互斥信号量初值一般为1,同步信号量的初始值要看对应资源的初始值是多少】
semaphore mutex=1; //互斥信号量,实现对缓冲区的互斥访问
semphore empty=n;  //同步信号量,表示空闲缓冲区的数量
semphore full=0; //同步信号了,表示产品的数量,也叫非空缓冲区的数量

producer(){
    while(1){
        生产一个产品;
        P(empty); //消耗一个空闲缓冲区
        P(mutex)//准备进入互斥,通过后禁止其他进程访问
            把产品放入缓冲区;
        V(mutex); //mutex=1,资源使用完毕,允许其他进程访问
        V(full); //经过生产,产品+1
        把产品放入缓冲区;
    }
}
consumer(){
    while(1){
        P(full);  //消耗一个产品(非缓冲区)
        P(mutex); //准备进入互斥,通过后禁止其他进程访问
        从缓冲区取出一个产品;
        V(mutex);  //解锁
        V(empty); //增加一个空闲缓冲区
    }
}

能否改变相邻P操作顺序?【先执行P(mutex)再执行P(empty/full)

答:不能。若先执行P(mutex),则会进入下一步并锁程序,但若此时是满的,则p(empty)无法执行【空时同理p(full)】,即会卡在此处,一直占用临界资源无法释放,后续进程也就无法对其进行调用,从而造成混乱。

2、读者写者问题

有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程或写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:
允许多个读者可以同时对文件执行读操作
只允许一个写者往文件中写信息
任一写者在完成写操作之前不允许其他读者或写者工作
写者执行写操作前,应让已有的读者和写者全部退出

为什么读进程可以并发,写进程一次却只允许一个?
答:因为读数据后不会将数据清空,读与读之间互不影响,因此可以并发;而写与写如果同时发生,后者写的内容会将前者覆盖,从而造成混乱;写和读同时进行时,可能造成读写内容不一致,造成混乱。

关系:写读互斥、写写互斥、读读不互斥

semaphroe rw=1;  //用于实现对文件的互斥访问
int count=0;  //记录当前有几个读进程在访问文件
semaphore mutex=1;  //用于保证对count变量的互斥访问
semaphore w=1;  //用于实现相对"写优先"

writer(){
    while(1){
        P(w);  //阻止读进程进入
        P(rw);  //互斥访问临界资源
        写文件...
        V(rw);
        V(w);
    }
}
reader(){
    while(1){
        P(w); //配合写进程,防止写进程饥饿
        P(mutex); //一次只允许一个读进程进行count操作。防止混乱
        if(count==0)
            P(rw); //加锁,此时写进程无法访问
        count++;
        V(mutex);
        V(w);
        读文件...
        P(mutex); //防止读进程结束的count--同时进行,从而造成V(mutex)无法进行
        count--;
        if(count==0)
            V(rw);
        V(mutex);
    }
}

读者①–>读者②–>写者③–>读者④

  1. 进入reader(),P(w)加锁,P(mutex)加锁【在释放前其他读进程无法进入】,此时count==0,所以P(rw)加锁【读写互斥】,count++后为1,表示此时有1个元素在访问文件,++操作结束后V(mutex)、V(w)解锁,进入读文件……此时还没有释放P(rw)
  2. 进入reader(),由于P(w)P(mutex)都解锁,因此可直接进入并加锁,同时因为count1,所以不会进入P(rw),因此不会被没有释放P(rw)而影响到,可以正常进行,count++后为2,解锁,进入读文件……假设步骤②速度比步骤①快,进入P(mutex)加锁,使进行count--时只有1个读进程在操作,避免混乱,执行后count1,不满足解锁rw条件,因此步骤②将资源归还后P(rw)依旧没有被释放,即依然有读进程在执行,写进程无法进入
  3. 写文件进入P(w)并为其加锁,P(rw)会因为读进程没有释放而无法进入,因此需要等读进程结束后释放出资源才能继续往下执行,从而实现读写互斥。假设此时前边的读进程都已释放,读进程进入写文件,P(rw)加锁……
  4. P(w)处无法进入,因为步骤③为其加锁且还未释放,必须等步骤③释放后才可进入,从而实现进程"先来先服务"原则,假设步骤③执行完毕,则步骤④变成一个单独的读进程,正常加锁解锁即可。
3、哲学家吃饭问题

一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆1根筷子,桌子的中间是1碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子【一根一根地拿起】。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

生产者消费者问题、读者写者问题、哲学家问题细致讲解_第1张图片

思路分析
要防止死锁发生,即每人拿起1根筷子

  1. 最多允许4个哲学家同时进餐,则最少有1个哲学家是可以吃饭的【原先每人1根正好分配,现在有1个人不拿筷子,因此肯定有1个人有两根筷子】
  2. 要求奇数号吃饭先拿起左边筷子;偶数号吃饭先拿起右边筷子,这样当相邻两位哲学家想吃饭时,1位会成功,另1位则被阻塞,避免了占有1只筷子后等待另1只筷子的情况。
  3. 仅当左右都有筷子时才允许他抓起筷子。
//以下为拿筷子时先加锁,两根筷子都到手才解锁的实现方式
semaphore chopstick[5]={1,1,1,1,1};  //初始筷子状态
semaphore mutex=1;  //互斥取筷子
Pi(){
    while(1){
        P(mutex); //互斥加锁
        P(chopstick[i]); //拿左边
        P(chopstick[(i+1)%5]); //拿右边
        V(mutex);  //拿完筷子解锁
        吃饭...
        V(chopstick[i]);  //吃完放左边筷子
        V(chopstick[(i+1)%5]); //放右边
        哲学家继续思考...
    }
}

你可能感兴趣的:(操作系统,操作系统)