OS_process_sync_producer&comsumer
reference
- 操作系统v4(汤)
生产者消费者问题
临界资源 (Critical Resouce)
许多硬件资源如打印机、磁带机等,都属于临界资源,诸进程间应采取互斥方式,实现对这种资源的共享。
下面我们将通过一个简单的例子来说明这一过程。
临界区(critical section)
- 由前所述可知,不论是硬件临界资源还是软件临界资源,多个进程必须互斥地对它进行访问。
- 人们把在每个进程中访问临界资源的那段代码称为
临界区(critical section)
。- (换句话说),显然地,若能保证诸进程
互斥地进入自己的临界区
,便可实现诸进程对临界资源的互斥访问。 - 为此,每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看它是否正被访问。
- (换句话说),显然地,若能保证诸进程
- 如果此刻临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问的标志;
- 如果此刻该临界资源正被某进程访问,则本进程不能进入临界区。
- 因此,必须在临界区前面增加一段用于进行上述检查的代码,把这段代码称为进入区(entry section)。
- 相应地,在临界区后面也要**加上一段称为退出区(exit section)**的代码,用于将临界区正被访问的标志恢复为未被访问的标志。
- 进程中除上述进入区、临界区及退出区之外的其它部分的代码在这里都称为剩余区。
这样,可把一个访问临界资源的循环进程描述如下:
while(TURE){
进入区
临界区
退出区
剩余区
}
The proceducer-consumer problem)描述
- 生产者-消费者(producer-consumer)问题是一个著名的进程同步问题。
- 它描述的是:
- 有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。
- 为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将其所生产的产品放入一个缓冲区中;
- 消费者进程可从一个缓冲区中取走产品去消费。
- 尽管所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,既不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品。
- 我们可利用一个数组
buffer
来表示上述的具有n个缓冲区的缓冲池。 - 每投入(或取出)一个产品时,缓冲池
buffer
中暂存产品(或已取走产品的空闲单元)的数组单元指针in(或out)加1。 - 由于这里由
buffer
组成的缓冲池是被组织成循环缓冲的,- 故应把输入指针in加1,表示成
in=(in+1)%n
- 把输出指针out+1表示成:
out=(out+1)%n
- 故应把输入指针in加1,表示成
- 当
(in+1)%n=out
时表示缓冲池满:而in=out
则表示缓冲池空。 - 此外,还引入了一个整型变量
counter
,其初始值为0。 - 每当生产者进程向缓冲池中投放(或取走)一个产品后,使counter加1(或减1)。
生产者和消费者两进程共享下面的变量:
int in=0, out=0, count=0;
item buffer[n];
- 指针
in
和out
初始化为0,在生产者进程中使用一局部变量nextp
,用于暂时存放每次刚刚生产出来的产品; - 而在消费者进程中,则使用一个局部变量
nextc
,用于存放每次要消费的产品
- 由于生产者-消费者问题是相互合作的进程关系的一种抽象,例如,在输入时,输入进程是生产者,计算进程是消费者;而在输出时,则计算进程是生产者,而打印进程是消费者,因此,该问题有很大的代表性及实用价值。
利用记录型信号量解决生产者消费者问题
-
假定在生产者和消费者之间的公用缓冲池中具有n个缓冲区,这时可利用互斥信号量
mutex
实现诸进程对缓冲池的互斥使用;- 利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。
- 又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将消息送入缓冲池;
- 只要缓冲池未空,消费者便可从缓冲池中取走一个消息。
对生产者消费者问题可描述如下:
int in=0, out=0;
item buffer[n];
semaphore mutex=1, empty=n, full=0;
void proceducer() {
do {
producer an item nextp;
...
wait(empty);
wait(mutex);
buffer[in]=nextp;
in :=(in+1) % n;
signal(mutex);
signal(full);
}while(TRUE);
}
void consumer() {
do {
wait(full);
wait(mutex);
nextc= buffer[out];
out =(out+1) % n;
signal(mutex);
signal(empty);
consumer the item in nextc;
...
}while(TRUE);
}
void main() {
cobegin
proceducer();
consumer();
coend
}
AND信号量&管程方式
AND信号量
-
对于生产者-消费者问题,也可利用AND信号量来解决,
-
即用Swait(empty,mutex)来代替wait(empty)和 wait(mutex);
-
用Ssignal(mutex, full)来代替signal(mutex)和signal(full):
-
用Swait(full,mutex)代替 wait(full)和 wait(mutex),
-
以及用Ssignal(mutex,empty)代替Signal(mutex)和 Signal(empty)。
管程方式
管程的一般优点
-
虽然信号量机制是一种既方便、又有效的进程同步机制,但每个要访问临界资源的进程都必须自备同步操作 wait(S)和 signal(S)。
-
这就使大量的同步操作分散在各个进程中。
-
这不仅给系统的管理带来了麻烦,而且还会因同步操作的使用不当而导致系统死锁。
-
这样,在解决上述问题的过程中,便产生了一种新的进程同步工具——管程(
Monitors
). -
在利用管程方法来解决生产者-消费者问题时,首先便是为它们建立一个管程,并命名为procducerconsumer,或简称为PC。其中包括两个过程:
- (1) put(x)过程。生产者利用该过程将自己生产的产品投放到缓冲池中,并用整型变量count来表示在缓冲池中已有的产品数目,当count≥N时,表示缓冲池已满,生产者须等待。
- (2) get(x)过程。消费者利用该过程从缓冲池中取出一个产品,当count≤0时,表示缓冲池中已无可取用的产品,消费者应等待。
-
对于条件变量
notfull
和notempty
,分别有两个过程cwait和 csignal对它们进行操作:- (1)
cwait(condition)
过程:当管程被一个进程占用时,其他进程调用该过程时阻塞,并挂在条件condition的队列上。 - (2)
csignal(condition)
过程**:唤醒**在cwait执行后阻塞在条件condition队列上的进程,如果这样的进程不止一个,则选择其中一个实施唤醒操作;如果队列为空,则无操作而返回。
- (1)
对应的管程定义
-
PC管程可描述如下: Monitor procducerconsumer { item buffer[N]; int in, out; condition notfull, notempty; int count; public: void put(item x) { if(count>=N) cwait(notfull); buffer[in]= x; in = (in+1) % N; count++; csignal(notempty); } void get(item x){ if (count<=0) cwait(notempty); x = buffer[out]; out = (out+1) % N; count--; csignal(notfull); } {in=0;out=O;count=O; } } PC;
管程对应的生产者和消费者定义
-
void producer() {item x; while(TRUE){ ... produce an item in nextp; PC.put(x); } } void consumer) { item x; while(TRUE){ PC.get(x); consume the item in nextc; .... } } void main() { cobegin proceducer(); consumer(); coend }