1.问题描述
系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中去除一个产品并使用。(这里的“产品”理解为某种数据)
生产者、消费者共享一个初始为空、大小为 n 的缓冲区。
只有缓冲区没满时,生产者才能把产品放入缓冲区。
只有缓冲区不空时,消费者才能从中取走产品,否则必须等待。
生产者进程每次生产一个产品,消费者进程每次取走一个产品。当缓冲区为满时,只能阻塞生产者进程,切换为消费者进程取走产品。只要缓冲区中又大于或等于 1 个可用的空间时就可以唤醒生产者进程继续生产。
当缓冲区为空,消费者进程就没有可以被取走的产品了,只能阻塞消费者进程,等待产品被放入缓冲区。
缓冲区属于临界资源,各个进程必须互斥地访问。如果不属于临界资源,则各生产者进程并发地执行时由于无法控制执行进度,有可能导致两个进程同时访问缓冲区的同一块区域导致数据冲突。
如何用信号量机制(P、V 操作)实现生产者、消费者进程的这些功能?
信号量机制可以实现互斥、同步、对一类系统资源的申请和释放。
互斥:设置初始值为 1 的互斥信号量。
同步:设置初始值为 0 的同步信号量(实现“一前一后”)
申请和释放资源:设置一个信号量,初始值为资源的数量(本质上也是属于“同步问题”,若无空闲资源,则申请自愿的进程需要等待别的进程释放资源后才能据需执行下去)
分析:
1.关系分析:找出题目中描述的各个进程,分析它们之间的同步关系。
只有缓冲区没满时,生产者才能把产品放入缓冲区。这个属于同步关系。缓冲区没满时,生产者要等待消费者取走产品。
只有缓冲区不空时,消费者才能从中取走产品,否则必须等待。这个也属于同步关系。缓冲区为空时(即没有产品时),消费者要等待生产者生产商品。
只有缓冲区不空时,消费者才能从中取走产品,否则必须等待。这个属于互斥关系。
2.整理思路,根据各进程的操作流程确定 P、V 操作的大致顺序。
生产者每次要消耗(P)一个空闲缓冲区,并生产(V)一个产品。消费者者每次要消耗(P)一个产品,并释放一个空闲的缓冲区(V)。往缓冲区放入,取走产品需要互斥。
3.设置信号量。设置需要的信号量,并根据题目题目条件确定信号量初值(互斥信号量初值一般为 1,同步信号量的初始值要看对应资源的初始值是多少)
semaphore mutex = 1; //互斥信号量,实现进程互斥访问。 semaphore empty = n; //同步信号量,表示空闲缓冲区的数量。 semaphore full = 0; //同步信号量,表示产品的数量,也即非空缓冲区的数量。
2.如何实现
根据上面设置的信号量,实现响应代码:
//生产者进程
producer(){
while(1){
生产一个产品;
P(empty); //消耗一个空闲缓冲区
P(mutex); //实现进程互斥
把产品放入缓冲区;
V(mutex);
V(full); //增加一个产品
}
}
//消费者进程
consumer(){
while(1){
P(full); //消耗一个产品(非空缓冲区),此P操作用于实现进程同步
P(mutex); //此P操作用于实现进程互斥
从缓冲区取走一个产品;
V(mutex);
V(empty); //增加一个空闲缓冲区
}
}
/*
P(mutex);
把产品放入缓冲区;
V(mutex);
上面的代码实现了进程互斥,实现互斥是在同一进程中进程一对PV操作
生产者进程的V(full)和消费者进程的P(full)用于实现两个进程的同步关系。即在其中一个一个进程中执行P,另一个进程中执行V
*/
3.思考:能否改变相邻 P,V 操作的顺序?
生产者生产产品和消费者消费产品从代码上看是放在 PV 操作之外。能不能放到 PV 操作之内呢?从逻辑上来看是没问题,可以让消费者从缓冲区中取出一个产品后就立即使用这个产品。但会导致临界区代码量变大,消费者进程在访问临界区的过程中就需要花费更多的时间,如果此时有别的进程也需要访问临界资源则消费者进程会被阻塞,会导致进程的并发度降低,尽量不要放入 PV 操作之中。