生产者消费者模型,C/C++实现(Linux)

来源:互联网 发布:mac 安装dmg到user 编辑:程序博客网 时间:2024/06/05 04:17

操作系统以及Linux上都详细地介绍了信号量以及锁的概念,其重要性可想而知,这事我写的一个比较简单的多线程实现的生产者消费者模型,我会对里面用到的函数等做出比较详细的解释,以方便解读。

#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <unistd.h>#include <semaphore.h>#define PRODUCER 5             //生产者数目#define CONSUMER 5             //消费者数目#define POOL_SIZE 11           //缓冲池大小int pool[POOL_SIZE];         int head = 0;                 //第一个产品的位置int rear = 0;                 //最后一个产品的位置sem_t room_sem;             //sem变量相当于,创建空余位置的信号量sem_t product_sem;      //商品数目的信号量,和上面一个一起配合同步pthread_mutex_t mutex;      //线程锁,控制互斥void *producer_fun()        //生产者函数,注意他的返回类型{    while(1)    {        sleep(1);        sem_wait(&room_sem);       //申请一个空闲区域,相当于P        pthread_mutex_lock(&mutex); //加锁,注意和P操作的顺序        pool[rear]=1;        printf("producer:%d\n",rear);        rear=(rear+1)%POOL_SIZE;        pthread_mutex_unlock(&mutex);//解锁,和V操作顺序没有要求        sem_post(&product_sem);//释放一个商品变量,V操作,相当于加1    }}void *consumer_fun()  //消费者函数,不解释了{    while(1)    {        sleep(2);        sem_wait(&product_sem);        pthread_mutex_lock(&mutex);        int data=pool[head];        printf("consumer:%d\n",head);        head=(head+1)%POOL_SIZE;        pthread_mutex_unlock(&mutex);        sem_post(&room_sem);    }}int main(){    pthread_t producer_id[PRODUCER];     //保存生产者id的    pthread_t consumer_id[CONSUMER];     //保存消费者id    pthread_mutex_init(&mutex,NULL);     //初始化锁    int ret=sem_init(&room_sem,0,POOL_SIZE-1); //初始化信号量,为空闲区大小    if(ret!=0)    {        printf("room_sem init error\n");        exit(-1);    }    ret =sem_init(&product_sem,0,0); //初始化信号,为0    if(ret!=0)    {        printf("producer_sem init error\n");        exit(-1);    }    for(int i=0;i<PRODUCER;i++)  //创建生产者线程    {        ret = pthread_create(&producer_id[i],NULL,producer_fun,NULL);        if(ret!=0)        {            printf("creat consumer pthread error!\n");        }    }    for(int i=0;i<CONSUMER;i++)  //创建消费者线程    {        ret = pthread_create(&consumer_id[i],NULL,consumer_fun,NULL);        if(ret!=0)        {            printf("creat consumer pthread error!\n");        }    }    //释放线程资源    for(int i=0;i<PRODUCER;i++)    {        pthread_join(producer_id[i],NULL);    }    for(int i=0;i<CONSUMER;i++)    {        pthread_join(consumer_id[i],NULL);    }}

上面代码除了我加的注释的地方外,别的需要说明的函数就两个

1、int pthread_create(pthread_t* tid, pthread_attr_t attr, void(start_routine)(void), void *arg);

tid:输出参数,用于返回所创建线程的标识;
attr:用于设定线程属性,大多数情况下传NULL;
start_routine:用于指定线程主函数
arg:为线程主函数传递的参数

我们看下这个函数的最后两个参数

void*(start_routine)(void):这个表示返回类型为void*,参数为void*的函数指针,如果城建的函数是其他类型,需要通过强制类型转化。

void arg:这是个传入的参数,但他最终不是传给pthread_create()。而是传给上面提到的那个绑定的void(start_routine)(void)。如果是其他类型的数据需要经过两次转化,如果需要多个参数可以通过结构体之类的数据结构包装。

2、int pthread_join(pthread_t tid, void **retval);

tid: 被等待的线程的id;
retval:出参,用于接收被等待线程结束时的返回值。

很多人可能对第二个参数比较奇怪,第二个参数相当于返回值,是一个出参,我们可以通过这个函数了解下:

int pthread_exit(void *retval);
retval: 线程结束时的返回值,用pthread_join可以获取该值。

这就比较好理解了。
为什么要回收线程退出状态呢,主要原因是linux有一种僵尸状态,在一个进程或者线程结束后,会有一个结构体,保存其退出是的状态等信息,只有在主线程回收以后他才会消失,当然,这里还有另外一种解决方式。

pthread_detach(pthread_self()) ;

他的作用是把该线程变为unjoinable,和主线程脱离关系,其在结束后会自动回收资源。

有讲的不对的地方希望指正,谢谢!!