一、概念

        在同一个进程里面有多个线程,这些线程分别扮演消费者和生产者两类。其中生产者为消费者提供任务,消费者去拿到任务并且执行任务。而他们之间的“交易场所”为内存块,即全局变量。当生产任务达到一定量满的时候,生产者进行等待,并且唤醒消费者进行拿取。当任务达到0的时候,消费者进行等待,并且唤醒生产者进行生产任务。

        在消费者和生产者中存在两种关系,其中生产者和生产者之间是互斥关系,消费者和消费者之间是互斥关系,生产者和消费者之间是互斥和同步的关系。

        生产数据和消费数据一定是成对出现的,生产一个消费一个,满了不生产,空了不消费,生产者不能无限生产,消费者也不能无限消费,符合生产者/消费者模型。生产者速度快,就不占用CPU时间片,等着消费者消费完通知它继续生产,这块时间片可以用来给其他线程用。

        生产者/消费者模型最终达到的目的是平衡生产者和消费者的处理能力,达到这个目的的过程中,并不要求只有一个生产者和一个消费者。可以多个生产者对应多个消费者,可以一个生产者对应一个消费者,可以多个生产者对应一个消费者。

        比方说有生产者A和生产者B,缓冲区由于空了,消费者处于WAITING。生产者B处于WAITING,生产者A被消费者通知生产,生产者A生产出来的产品本应该通知消费者,结果通知了生产者B,生产者B被唤醒,发现缓冲区满了,于是继续WAITING。至此,两个生产者线程处于WAITING,消费者处于WAITING,系统假死。

二、优点

  • 耦。因为多了一个缓冲区,所以生产者和消费者并不直接相互调用,这一点很容易想到,这样生产者和消费者的代码发生变化,都不会对对方产生影响,这样其实就把生产者和消费者之间的强耦合解开,变为了生产者和缓冲区/消费者和缓冲区之间的弱耦合
  • 通过平衡生产者和消费者的处理能力来提高整体处理数据的速度,这是生产者/消费者模型最重要的一个优点。如果消费者直接从生产者这里拿数据,如果生产者生产的速度很慢,但消费者消费的速度很快,那消费者就得占用CPU的时间片白白等在那边。有了生产者/消费者模型,生产者和消费者就是两个独立的并发体,生产者把生产出来的数据往缓冲区一丢就好了,不必管消费者;消费者也是,从缓冲区去拿数据就好了,也不必管生产者,缓冲区满了就不生产,缓冲区空了就不消费,使生产者/消费者的处理能力达到一个动态的平衡
  • 效率高
  • 方便代码维护

三、实战

        如以下程序所示,生产者和消费者各位3个线程。生产者负责随机产生两个随机数进行相加,数据存放在全局变量的结构体中。因为生产者和生产者之间是互斥的,消费者和消费者之间是互斥的,生产者和消费者之间是互斥与同步的。所以我们设置了三个互斥锁来分别处理生产者之间、消费者之间、生产者和消费者之间的互斥,以及设置了两个条件变量来确保,生产者和消费者之间的同步。

       实现的是多生产者和多消费者的模型。 

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>

struct Test{
        int x;
        int y;
        int z;
}buf[10];

pthread_mutex_t mutex1,mutex2,mutex3;
pthread_cond_t cond1,cond2;

int j = 0;
int n = 0;
int num = 0;

void productor_lock()
{
        if(pthread_mutex_lock(&mutex1) != 0){
                printf("productor lock error\n");
        }
}

void consumer_lock()
{
        if(pthread_mutex_lock(&mutex3) != 0){
                printf("consumer lock error\n");
        }
}

void data_lock()
{
        if(pthread_mutex_lock(&mutex2) != 0){
                printf("data lock error\n");
        }
}

void productor_unlock()
{
        if(pthread_mutex_unlock(&mutex1) != 0){
                printf("productor unlock error\n");
        }
}
void consumer_unlock()
{
        if(pthread_mutex_unlock(&mutex3) != 0){
                printf("consumer unlock error\n");
        }
}

void data_unlock()
{
        if(pthread_mutex_unlock(&mutex2) != 0){
                printf("data unlock error\n");
        }
}

void productor_wait()
{
        n = 1;
        if(pthread_cond_wait(&cond1,&mutex3) != 0){
                printf("prodacut wait error\n");
        }
}

void consumer_wait()
{
        j = 1;
        if(pthread_cond_wait(&cond2,&mutex1) != 0){
                printf("consumer wait error\n");
        }
}

void productor_wakeup()
{
        n = 0;
        if(pthread_cond_signal(&cond1) != 0){
                printf("productor wakeup error\n");
        }
        printf("productor wakeup successs\n");
}
void consumer_wakeup()
{
        j = 0;
        if(pthread_cond_signal(&cond2) != 0){
                printf("consumer wakeup error\n");
        }
        printf("consumer wakeup success\n");
}

void* consumer(void *argc)
{
        int i;

        while(1){
                consumer_lock();

                sleep(1);

                if(num == 0){
                        printf("consumer wait\n");
                        consumer_wait();
                }

                data_lock();

                if(num == 10){
                        printf("consume %d + %d = %d\n",buf[9].x,buf[9].y,buf[9].x+buf[9].y);
                        printf("num = %d\n",num--);
                        if(n == 1){
                                productor_wakeup();
                        }
                }else{
                        printf("consume %d + %d = %d\n",buf[num-1].x,buf[num-1].y,buf[num-1].x+buf[num-1].y);
                        printf("num = %d\n",num--);
                }

                data_unlock();
              consumer_unlock();

                usleep(500);
        }
        pthread_exit(NULL);
}

void* productor(void *argc)
{
        while(1){
                int X = abs(rand() % 100);
                int Y = abs(rand() % 100);

                productor_lock();

                sleep(1);

                if(num == 10){
                        printf("productor wait\n");
                        productor_wait();
                }

                data_lock();

                if(num <= 0){
                        buf[0].x = X;
                        buf[0].y = Y;
                        printf("product %d + %d = ?\n",buf[0].x,buf[0].y);
                        printf("num = %d\n",num++);
                        if(j == 1){
                                consumer_wakeup();
                        }
                }else{
                        buf[num].x = X;
                        buf[num].y = Y;
                        printf("product %d + %d = ?\n",buf[num].x,buf[num].y);
                        printf("num = %d\n",num++);
                        if(j == 1){
                                consumer_wakeup();
                        }
 }

                data_unlock();
                productor_unlock();

                usleep(500);
        }

        pthread_exit(NULL);
}

int main()
{
        int i;
        int ret;
        pthread_t prod[3],cons[3];

        pthread_mutex_init(&mutex1,NULL);
        pthread_mutex_init(&mutex2,NULL);
        pthread_mutex_init(&mutex3,NULL);

        pthread_cond_init(&cond1,NULL);
        pthread_cond_init(&cond2,NULL);

        for(i=0;i<3;i++){
                ret = pthread_create(&prod[i],NULL,productor,NULL);
                if(ret < 0){
                        printf("productor%d create error\n",i);
                }
        }
        for(i=0;i<3;i++){
                ret = pthread_create(&cons[i],NULL,consumer,NULL);
                if(ret < 0){
                        printf("consumer%d create error\n",i);
                }
        }

        for(i=0;i<3;i++){
                pthread_join(prod[i],NULL);
        }

        for(i=0;i<3;i++){
                pthread_join(cons[i],NULL);
        }

        pthread_mutex_destroy(&mutex1);
        pthread_mutex_destroy(&mutex2);
        pthread_mutex_destroy(&mutex3);

        pthread_cond_destroy(&cond1);
        pthread_cond_destroy(&cond2);

        return 0;
}