1.生产者-消费者问题
生产者和消费者问题在现实系统中是很普遍的。例如在一个多媒体系统中,生产者编码视频帧,而消费者消费(解码)视频帧,缓冲区的目的就是减少视频流的抖动。又如在图形用户接口设计中,生产者检测到鼠标和键盘事件,并将其插入到缓冲区中。消费者以某种基于优先级的方式从缓冲区中取出这些事件并显示在屏幕上。
生产者和消费者模式共享一个有n个槽位的有限缓冲区。生产者反复地生成新的item,并将它插入到缓冲区中。消费者不断从缓冲区中取出这些item,并消费它们。它有3个显著特点:
- 因为生产和消费都涉及共享变量的更新,所以必须保证对缓冲区的访问是互斥的。
- 如果缓冲区是满的,那么生产者必须等待直到有一个槽位可用。
- 如果缓冲区是空的,那么消费者必须等待直到有一个item可以。
下文将开发一个简单的生产者消费者包SBUF,它操作类型为sbuf_t
的有限缓冲区。item存放在一个动态分配的容量为n的整数数组buf中。索引值front和rear分别指向数组的首尾项。三个信号量同步对缓冲区的访问。mutex
信号量提供互斥访问,slots
和items
信号量分别记录空槽位和可用item的数量。
typedef struct{
int *buf; //缓冲区指针(指向一个数组)
int n; //最大空槽位数量(缓冲区大小)
int front; //指向数组第一个item,即buf[(front+1)%n]
int rear; //指向数组最后一个item,即buf[rear%n]
sem_t mutex; //缓冲区互斥锁
sem_t slots; //可用槽位数
sem_t items; //可用item数
}sbuf_t;
Posix标准定义的信号量操作函数:
```c
include
int sem_init(sem_t sem, 0, unsigned int value); int sem_wait(sem_t s); //等价于P(s) int sem_post(sem_t *s); //等价于V(s) ```
下文继续给出SBUF包实现的代码:
- sbuf_init函数初始化一个缓冲区,在其它任意函数前被调用;
- sbuf_deinit函数释放一个缓冲区,在其它任意函数后被调用;
- sbuf_insert函数等待一个可用槽位并添加item;
- sbuf_remove函数等待并消费一个item;
/* $begin sbufc */
#include "csapp.h"
#include "sbuf.h"
/* Create an empty, bounded, shared FIFO buffer with n slots */
/* $begin sbuf_init */
void sbuf_init(sbuf_t *sp, int n)
{
sp->buf = Calloc(n, sizeof(int));
sp->n = n; /* Buffer holds max of n items */
sp->front = sp->rear = 0; /* Empty buffer iff front == rear */
Sem_init(&sp->mutex, 0, 1); /* Binary semaphore for locking