操作系统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; //数量