生产者-消费者问题

生产者-消费者问题是进程间或线程间同步的经典问题。该问题有多种实现,如消息队列、共享内存等,这次我们用的是多线程和互斥锁以及条件变量来实现该问题。


动手写程序之前先得把问题想明白。


1.该问题难在什么地方?

难点其实只有一个,就是同步问题。这个同步涉及了以下几个方面:多个生产者之间的同步(如:生产资源的总数的同步,生产的资源不会乱序的同步)、多个消费者之间的同步(如:消费资源的总数的同步,现有资源数目的同步)、生产者与消费者之间的同步(如:若现有资源为0,消费者要等生产者生产后才能继续消费)


2.如何解决上述的同步问题?

解决方法就是使用互斥锁和条件变量。如何使用呢?

1.可以将生产的资源相关信息和互斥锁A捆绑在一起,这样生产者生产之前先获取互斥锁A,再进行生产,更新资源信息。这便解决了生产者间的同步问题。

2.可以将消费资源相关信息和互斥锁B捆绑在一起,这样消费者消费之前先获取互斥锁B,再进行消费,更新消费信息。这便解决了消费者间的同步问题。

3.生产者和互斥锁直接的同步呢?这就是条件变量发挥作用的地方。消费者消费时,若发现现有资源数为0,则使用条件变量等待生产者生产资源,生产者生产资源后,便可通过条件变量通知消费者继续消费。


3.代码


要实现的过程:
1.创建生产者线程进行生产,若生产总数达到要求,则停止生产,终止生产者线程。
2.创建消费者线程进行消费,若已消费资源总数为要求生产总数,且现有资源数为0,则停止消费,终止消费者线程。若只是资源数为0,则等待生产者生产后继续消费。
3.生产者只有在现有资源数从0变为1才通知消费者消费;消费者也只在现有资源数为0时才等待生产者生产。

生产任务是对buff数组按顺序赋值,比如buff[i] = i。消费任务就是将现有资源数减1。这些都可以换成其他任务。

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

/* 任务数 */
#define MAXITEMS 1000000
/* 生产者线程数 */
#define MAXPRODUCE 100
/* 消费者数目 */
#define MAXCONSUME 10

typedef struct shared_t
{
    pthread_mutex_t mutex; //互斥锁

    //任务
    int buff[MAXITEMS];    //任务数组
    int nput;              //已生产资源数
    int nval;              //任务值
}put;
put task = {PTHREAD_MUTEX_INITIALIZER};

typedef struct nready_t    //若生产者生产的资源数为0,则阻塞消费者
{
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    int n;    //可消费资源数目
    int eat;    //已消费数目
}nready;
nready ready = {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};

void *produce(void *arg);    //生产程序
void *consume(void *arg);    //消费程序

int main()
{
    int i;
    int count[MAXPRODUCE];    //每个生产者已生产资源数目
    pthread_t tid_produce[MAXPRODUCE];    //生产者线程ID
    pthread_t tid_consume[MAXCONSUME];    //消费者线程ID

    pthread_setconcurrency(MAXPRODUCE + MAXCONSUME);    //设置并发线程数
    for (i = 0; i < MAXPRODUCE; i++)
    {
        count[i] = 0;
        pthread_create(&tid_produce[i], NULL, produce, (void *)&count[i]);    //创建并启动生产者线程
    }

    for (i = 0; i < MAXCONSUME; i++)
        pthread_create(&tid_consume[i], NULL, consume, NULL);    //创建并启动消费者线程

    int total_produce = 0; //总任务数
    for (i = 0; i < MAXPRODUCE; i++)
    {
        pthread_join(tid_produce[i], NULL);
        printf("count[%d] = %d\n", i, count[i]);    //输出每个生产者线程工作次数
        total_produce += count[i];
    }
    printf("total_produce = %d\n", total_produce);

    int total_consume = 0;
    for (i = 0; i < MAXCONSUME; i++)
        pthread_join(tid_consume[i], NULL);
    total_consume += ready.eat;
    printf("total_consume = %d\n", total_consume);

    return 0;
}

void *produce(void *arg)
{
    for (;;)
    {
        pthread_mutex_lock(&task.mutex);
        if (task.nput >= MAXITEMS)    //生产任务已完成
        {
            pthread_mutex_unlock(&task.mutex);
            return NULL;
        }
        else
        {   //生产线程进行生产工作
            task.buff[task.nput] = task.nval;    //资源按顺序赋值
            task.nput++;
            task.nval++;
            pthread_mutex_unlock(&task.mutex);
            (*(int *)arg)++;    //对已生产任务进行计数

            int zero = 0;
            pthread_mutex_lock(&ready.mutex);
            if (ready.n == 0)
                zero = 1;
            ready.n++;    //消费者可消费数目
            pthread_mutex_unlock(&ready.mutex);
            if (zero)
                pthread_cond_signal(&ready.cond);    //只在资源由0变为1时才通知消费者
        }
    }
}

void *consume(void *arg)
{
    int i;
    for (;;)
    {
        pthread_mutex_lock(&task.mutex);
        pthread_mutex_lock(&ready.mutex);
        if (task.nput >= MAXPRODUCE && ready.n == 0)    //生产者生产完毕且可消费资源数为0时,退出
        {
            pthread_mutex_unlock(&task.mutex);
            pthread_mutex_unlock(&ready.mutex);
            return NULL;
        }
        pthread_mutex_unlock(&task.mutex);
        while (ready.n == 0)    //可消费数为0
            pthread_cond_wait(&ready.cond, &ready.mutex);    //等待生产者生产,并通知
        ready.n--;    //消费者消费资源
        ready.eat++;    //增加已消费数目
        pthread_mutex_unlock(&ready.mutex);
    }
}


4.分析代码

其实把问题和生产消费过程想清楚也没什么好分析的,需要注意的一点是main函数内的
pthread_setconcurrency(MAXPRODUCE + MAXCONSUME);

这函数比较少见,但很重要,该函数是设置并发线程数的。为什么要设置呢,难道不是创建的所有线程都会执行吗?还真不是,有些系统可能你创建了很多的线程,但实际情况是就第一个线程在执行,其他线程都在打酱油。


实验结果


...中间省略若干行





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值