操作系统实验——PV操作实现生产者消费者模型

操作系统PV操作之——生产者消费者模型

个人博客主页
参考资料:
Java实现PV操作 | 生产者与消费者

浙大公开课

操作系统的多进程、多线程操作中经常会有因为同步、互斥等等问题引发出的一系列问题,我们的前辈为了解决这些问题,发明出了“信号量(Semaphore)”这么一个令人称奇的变量,就目前来看,很巧妙的解决了这些问题。

信号量是个整形变量

信号量S只允许两个标准操作wait()和signal(),或者他的发明者称呼的P操作和V操作

wait()和signal()是原子操作,不可分割的原语

对PV操作的定义

对PV操作的定义不是单一的,这里举个比较简单的例子

/*P操作*/ wait(S){ value--; if(value < 0){ /*value的大小表示了允许同时进入临界区进行操作的 进程数量*/ /*add this process to waiting queue*/ block(); } } /*V操作*/ signal(S){ value++; if(value <= 0){ /*因为P操作是当value<0时休眠一个线程,说明如果有休眠 的线程,则value一定小于0,所以此时当value+1后,如果 还有休眠的线程,value必定小于或等于0 */ /*remove a process P from the waiting queue*/ wakeup(P); } } 信号量的应用

临界区(互斥)问题,信号量初值需要置为1,表示只能有一个进程进入临界区,从而保护临界区内的数据同时只能被一个进程访问,避免出现多个进程同时操作同一个数据。

Semaphore S; //初始值为1 do{ wait(S); Critical Section; //临界区 signal(S); remainder section //剩余部分 }while(1);

两个进程的同步问题。假如有两个进程 Pi和Pj,Pi有个A语句(输入x的值),Pj有个B语句(输出x+1的值),希望在B语句执行之前A语句已经执行完成。

//同步,定义信号量flag初值为0,等待方用wait操作,被等待方用signal操作,还要紧贴着放 Pi进程 Pj进程 ... ... A wait(flag) signal(flag) B ... ...

生产者消费者模型 先定义出PV操作的类 /** * 封装的PV操作类,为了简单起见,没有用一个等待队列,而是直接用 * Java的Object类方法中的wait方法来模拟 * @author Vfdxvffd * @count 信号量 * 这里调用wait方法和signal方法的是同一个对象this,所以V操作唤 * 醒的只能是同一个对象P操作加入等待队列的进程 */ class syn{ int count = 0; syn(){} syn(int a){count = a;} //给信号量赋初值 public synchronized void Wait() { count--; if(count < 0) { //block /*add this process to waiting queue*/ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void Signal() { count++; if(count <= 0) { //wakeup /*remove a process P from the waiting queue*/ notify(); } } } 单生产单消费(PV操作解决同步问题)

先引入全局的信号量,将其封装在一个类中

class Global{ static syn empty = new syn(2); //成员变量count表示剩余空闲缓冲区的数量, >0则生产者进程可以执行 static syn full = new syn(0); //成员变量count表示当前待消费物品的数量, >0则消费者进程可以执行 static int[] buffer = new int[2]; //缓冲区数组,大小代表缓冲区的数量,即放面包的盘子 }

生产者类

/** * 单个生产者类 * @author Vfdxvffd * @count 生产的物品数量标号 */ class Producer implements Runnable{ int count = 0; //数量 @Override public void run() { while(count < 20) { //最多生产20件商品 Global.empty.Wait(); /*要生产物品了,给剩余空 闲缓冲区数量--,如果减完后变为负数,则说明当前没 有空闲缓冲区,则加入等待队列*/ //临界区,生产商品 int index = count % 2; Global.buffer[index] = count; System.out.println("生产者在缓冲区"+index+"中生产了物品"+count); count++; /*可以在此处让进程休眠几秒钟,然后就可能在此时 CPU将资源调给消费者,但是可以发现由于下面语句还 未执行,所以消费者拿到CPU执行权也只能消费掉前几 次生产的商品,这次生产的商品依旧无法被消费*/ Global.full.Signal();/*发出一个信号,表示缓冲区已 经有物品了,可以来消费了,成员变量count的值表示缓冲 区的待消费物品的数量,相当于唤醒消费者*/ } } }

消费者类

/** * 单个消费者类 * @author Vfdxvffd * @count 物品数量标号 */ class Consumer implements Runnable{ int count = 0; @Override public void run() { while(count < 20) { Global.full.Wait(); /*要消费物品了,给当前待消费 物品--,如果减完为负数,则说明当前没有可消费物品, 加入等待队列*/ //临界区 int index = count % 2; int value = Global.buffer[index]; System.out.println("消费者在缓冲区"+index+"中消费了物品"+value); count++; /*可以在此处让进程休眠几秒钟,然后就可能在此时CPU将 资源调给生产者,但是可以发现由于下面语句还未执行, 所以生产者拿到CPU执行权也只能生产在前几次消费的商品 腾出的缓冲区,这次消费的商品腾出的地方依旧无法被用 于生产*/ Global.empty.Signal(); /*消费完一个物品后,释放 一个缓冲区,给空闲缓冲区数量++,唤醒生产者可以生产 商品了*/ } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppxyg.html