写该篇博客原因:生产者-消费者模型挺常用的,笔者记性又不好,人也比较笨,只好自己写着玩,以后自己用自己看自己写的博客就是。╮(๑•́ ₃•̀๑)╭
本博客为参照书《深入理解计算机系统》来通过信号量来实现生产者—消费者模型。
生产者—消费者模型:生产者和消费者线程共享一个有n个槽(slots)的有限缓冲区。生产者不停的生产新的项目(item),并把它们插入到缓冲区中。消费者线程不断地从缓冲区取出这些项目,然后消费他们。
要解决的问题:因为插入和取出缓冲区的项目都需要更新共享变量,所以我们必须保证对缓冲区的访问是互斥的,并且需要保证缓冲区所有的数据都被消费者访问到。
在本例中,通过信号量来实现对缓冲区可写空槽位数量(生产者)和可读槽位数量(消费者)的计数(原子操作)。
在参照书后,写出一份C代码和一份C++代码,其中C++代码使用vector容器类存放数据,扩展性好。
代码如下(C):https://download.csdn.net/download/ty1121466568/10775953
C实现:
编译代码命令(懒得写makefile了):gcc -o test test.c producer_consumer_buf.c -lpthread
头文件:producer_consumer_buf.h(定义对该模型的结构体,方便之后操作)
#ifndef _PRODUCER_CONSUMER_BUF_H_
#define _PRODUCER_CONSUMER_BUF_H_
#include <stdio.h>
#include <semaphore.h>
#define P(sem) sem_wait(sem)
#define V(sem) sem_post(sem)
#define PV_INIT(sem,pshared,value) sem_init(sem,pshared,value)
typedef struct producer_consumer_buf
{
int *buf; /*buffer array*/
int slots_num; /*max num slots*/
int p_pos; /*producer pos of buf*/
int c_pos; /*consumer pos of buf*/
sem_t mutex; /*protects accesses to buf*/
sem_t slots; /*producer can write num*/
sem_t items; /*consumer can read num*/
}pcbuf_t;
int pcbuf_init(pcbuf_t *sp, int n);
int pcbuf_deinit(pcbuf_t *sp);
void pcbuf_insert(pcbuf_t *sp, int item);
void pcbuf_remove(pcbuf_t *sp, int *item);
#endif
c文件:producer_consumer_buf.c(对生产者消费者模型的代码实现)
#include <stdlib.h>
#include <string.h>
#include "producer_consumer_buf.h"
/*
function:init the model struct
params:model struct
*/
int pcbuf_init(pcbuf_t *sp, int n)
{
sp->buf = (int *)malloc(sizeof(int)*n);
if(sp->buf == NULL)
{
printf("can not malloc buf\n");
return -1;
}
memset(sp->buf,0x00,sizeof(int)*n);
sp->slots_num = n;
sp->slots_num = n;
sp->p_pos = sp->c_pos = 0;
PV_INIT(&sp->mutex,0,1); /*pshared=0 only this process*/
PV_INIT(&sp->slots,0,n);
PV_INIT(&sp->items,0,0);
return 0;
}
/*
function:destruct the model struct
params:model struct
*/
int pcbuf_deinit(pcbuf_t *sp)
{
free(sp->buf);
return 0;
}
/*
function:producer
params:model struct,in num
*/
void pcbuf_insert(pcbuf_t *sp, int item)
{
P(&sp->slots); /*wait for available slot*/
P(&sp->mutex); /*lock the buffer*/
sp->buf[(sp->p_pos++)%(sp->slots_num)] = item; /*insert the item*/
V(&sp->mutex); /*unlock the buffer*/
V(&sp->items); /*update items available num*/
}
/*
function:consumer
params:model struct,out num
*/
void pcbuf_remove(pcbuf_t *sp, int *item)
{
P(&sp->items); /*wait for available items*/
P(&sp->mutex); /*lock the buffer*/
*item = sp->buf[(sp->c_pos++)%(sp->slots_num)]; /*remove the item*/
V(&sp->mutex); /*unlock the buffer*/
V(&sp->slots); /*update slots available num*/
}
c文件:test.c(多线程编程实现测试)
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "producer_consumer_buf.h"
void *producer(void *vargp);
void *consumer(void *vargp);
int main(int argc, char **argv)
{
pthread_t tid1, tid2;
pcbuf_t sbuf;
pcbuf_init(&sbuf, 100);
pthread_create(&tid1, NULL, producer, (void *)&sbuf);
pthread_create(&tid2, NULL, consumer, (void *)&sbuf);
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
exit(0);
}
void *producer(void *vargp)
{
int i = 1000;
while(i--)
{
pcbuf_insert((pcbuf_t *)vargp, i);
printf("insert pos=%d i=%d \n",(((pcbuf_t *)vargp)->p_pos)-1,i);
}
return NULL;
}
void *consumer(void *vargp)
{
int i = 0;
while(1)
{
pcbuf_remove((pcbuf_t *)vargp, &i);
printf("consume pos=%d i=%d\n",(((pcbuf_t *)vargp)->c_pos)-1,i);
}
return NULL;
}
C++代码:由于打印存在延时之类,所以看到的打印信息并不严格,若要准确看到读写信息应该到临界段去打印。
编译选项:g++ -std=c++11 -pthread -o test test.cpp
类定义:producer_consumer_buf.h
#ifndef _PRODUCER_CONSUMER_BUF_H_
#define _PRODUCER_CONSUMER_BUF_H_
#include <stdio.h>
#include <semaphore.h>
#include <vector>
using namespace std;
#define P_SEM(sem) sem_wait(sem)
#define V_SEM(sem) sem_post(sem)
#define PV_SEM_INIT(sem,pshared,value) sem_init(sem,pshared,value)
template <typename T>
class sem_solt
{
private:
vector<T> v_solts;
int slots_num; /*max num slots*/
int p_pos; /*producer pos of buf*/
int c_pos; /*consumer pos of buf*/
sem_t mutex; /*protects accesses to buf*/
sem_t slots; /*producer can write num*/
sem_t items; /*consumer can read num*/
public:
sem_solt(int snum = 1);
void sem_insert(T item);
void sem_remove(T &item);
};
template<class T>
sem_solt<T>::sem_solt(int snum)
{
if(snum>0)
{
v_solts.resize(snum);
}
slots_num = snum;
p_pos = c_pos = 0;
PV_SEM_INIT(&mutex,0,1); /*pshared=0 only this process*/
PV_SEM_INIT(&slots,0,snum);
PV_SEM_INIT(&items,0,0);
}
template<class T>
void sem_solt<T>::sem_insert(T item)
{
P_SEM(&slots); /*wait for available slot*/
P_SEM(&mutex); /*lock the buffer*/
v_solts[(p_pos++)%(slots_num)] = item; /*insert the item*/
V_SEM(&mutex); /*unlock the buffer*/
V_SEM(&items); /*update items available num*/
}
template<class T>
void sem_solt<T>::sem_remove(T &item)
{
P_SEM(&items); /*wait for available items*/
P_SEM(&mutex); /*lock the buffer*/
item = v_solts[(c_pos++)%(slots_num)]; /*remove the item*/
V_SEM(&mutex); /*unlock the buffer*/
V_SEM(&slots); /*update slots available num*/
}
#endif
测试:test.c
#include <thread>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "producer_consumer_buf.h"
using namespace std;
void *producer(void *vargp);
void *consumer(void *vargp);
typedef struct str_test
{
int *buf;
int slots_num;
int p_pos;
int c_pos;
}st_t;
st_t temp;
sem_solt<str_test> newsolt(100);
int main(int argc, char **argv)
{
thread tid1(producer,(void *)NULL);
thread tid2(consumer,(void *)NULL);
tid1.join();
tid2.join();
exit(0);
}
void *producer(void *vargp)
{
int i = 1000;
while(i--)
{
temp.slots_num=i;
newsolt.sem_insert(temp);
printf("producer:%d\n",i);
}
return NULL;
}
void *consumer(void *vargp)
{
int i = 1000;
while(i--)
{
newsolt.sem_remove(temp);
printf("consumer:%d\n",temp.slots_num);
}
return NULL;
}