linux c 生产者与消费者问题 信号量(sem_t)

参考:“深入理解计算机系统” 第670页


信号量:

信号量提供两种特殊得操作P(s)与V(s)。

P(s):如果s为非零,P将s减一,然后立即返回。如果s为零,那么就挂起线程,直到s变为非零,而V操作会重启这条线程。在重启之后,P操作将s减1,并将控制权返回给调用者。

V(s):V操作将s加1。如果有任何线程P等待s变为非零的话,那么V就会重启其中一条线程,然后将程序的s减一,完成它的P操作。

在 linux semaphore.h 中的P和V操作, P(s)=sem_wait(sem_t*),V(s)=sem_post(sem_t*),下面我们用到的。


生产者与消费者问题:

1。什么是生产者与消费者问题?就是生产者和消费者共用一个缓冲区,生产者生产项目放进缓冲区,而消费者则从缓冲区中消费项目。当缓冲区满了的时候,生产者不能对其生产;而当缓冲区为空的时候,消费者不能作消费。


2。这个问题在现实系统中也运用得非常广泛。例如,在一个多媒体系统中,生产者编码视频帧,而消费者解码并在屏幕上呈现出来。缓冲区得目的是为了减少视频流的抖动,而这种抖动是由各个帧的编码和解码时与数据相关的差异引起得。缓冲区为生产者提供了一个槽位置,而为消费者提供了一个以编码的帧池。


实战代码:

缓冲区结构:

typedef struct __Buf {
    int *buf;       // 缓冲区
    int n;          // 最大缓冲区
    int head;       // 缓冲区头
    int tail;       // 缓冲区尾
    sem_t items;    // 锁定对不存在项目的缓冲区进行操作的消费者
    sem_t slots;    // 锁定对已满缓冲区进行操作的生产者
    sem_t mutex;    // 确保同时只能有一个消费者或生产者对缓存区进行操作
} Buf;

生产与消费:

// 生产者插入项目
void insert(Buf *b, int item) {
    sem_wait(&b->slots);
    sem_wait(&b->mutex);
    // 插入数据
    b->buf[(++b->tail)%b->n] = item;
    sem_post(&b->mutex);
    // post等价於把信号量加一,令消费者能对缓冲区操作(说明缓冲区状态为非空)
    sem_post(&b->items);
}

// 消费者获取项目
int get(Buf *b) {
    int item;
    sem_wait(&b->items);
    sem_wait(&b->mutex);
    // 获取数据(意义上是删除了缓冲区中一个项目)
    item = b->buf[(++b->head)%b->n];
    sem_post(&b->mutex);
    // post等价于把信号量加一,令生产者能对缓冲区操作(说明缓冲区状态为未满)
    sem_post(&b->slots);
    return item;
}

完整代码:

#include 
#include 
#include 
#include 
#include 

typedef struct __Buf {
    int *buf;       // 缓冲区
    int n;          // 最大缓冲区
    int head;       // 缓冲区头
    int tail;       // 缓冲区尾
    sem_t items;    // 锁定对不存在项目的缓冲区进行操作的消费者
    sem_t slots;    // 锁定对已满缓冲区进行操作的生产者
    sem_t mutex;    // 确保同时只能有一个消费者或生产者对缓存区进行操作
} Buf;

// 声明缓冲区
Buf g_buf;

// 生产者插入项目
void insert(Buf *b, int item) {
    sem_wait(&b->slots);
    sem_wait(&b->mutex);
    // 插入数据
    b->buf[(++b->tail)%b->n] = item;
    sem_post(&b->mutex);
    // post等价於把信号量加一,令消费者能对缓冲区操作(说明缓冲区状态为非空)
    sem_post(&b->items);
}

// 生产者线程
void *manufacturer(void *) {
    int i=0;
    for(i=0; i<100; ++i) {
        insert(&g_buf, i);
    }
    return 0;
}

// 消费者获取项目
int get(Buf *b) {
    int item;
    sem_wait(&b->items);
    sem_wait(&b->mutex);
    // 获取数据(意义上是删除了缓冲区中一个项目)
    item = b->buf[(++b->head)%b->n];
    sem_post(&b->mutex);
    // post等价于把信号量加一,令生产者能对缓冲区操作(说明缓冲区状态为未满)
    sem_post(&b->slots);
    return item;
}

// 消费者线程
void *consumer(void *) {
    int i=0, data;
    for(i=0; i<100; ++i) {
        data = get(&g_buf);
        printf("consumer: %d\n", data);
        // 消费者睡300ms, 测试一下看看生产者会不会把缓冲区溢出
        usleep(1000*300);
    }
    return 0;
}

int main() {
    pthread_t tid_man, tid_con;

    // 设置缓冲区
    g_buf.n = 5;
    g_buf.buf = (int *)malloc(g_buf.n*sizeof(int));
    sem_init(&g_buf.mutex, 0, 1);
    sem_init(&g_buf.slots, 0, g_buf.n);	// g_buf.n也就是允许插入的数量
    sem_init(&g_buf.items, 0, 0);	
    g_buf.head = -1;
    g_buf.tail = -1;

    // 创建生产者与消费者线程
    pthread_create(&tid_man, 0, manufacturer, 0);
    pthread_create(&tid_con, 0, consumer, 0);

    // 释放已申请的资源
    pthread_join(tid_man, 0);
    pthread_join(tid_con, 0);
    sem_destroy(&g_buf.items);
    sem_destroy(&g_buf.slots);
    sem_destroy(&g_buf.mutex);

    return 0;
}

备注:本人编程新手,如有出错请多多指点,谢谢。


你可能感兴趣的:(liunx,c/c++)