C语言条件变量和信号量两种方式实现生产者消费者问题

一、使用互斥锁和条件变量实现

三种思路都可以,推荐第1种简单好记:

1、万金油思路,生产者和消费者都有条件等待和唤醒

生产者拿到互斥锁后,检查缓冲区的产品数量:
如果缓冲区是满的,则生产者条件等待
如果缓冲区不是满的,就生产,然后发送信号唤醒消费者消费,释放互斥锁

消费者拿到互斥锁后,检查缓冲区的产品数量:
如果缓冲区是空的,则消费者条件等待
如果缓冲区不是空的,就消费,然后发送信号唤醒生产者生产,释放互斥锁

2、只有消费者条件等待,生产者负责唤醒

生产者拿到互斥锁后,检查缓冲区的产品数量:
如果缓冲区不是满的,就生产,然后发送信号唤醒消费者消费,释放互斥锁

消费者拿到互斥锁后,检查缓冲区的产品数量:
如果缓冲区是空的,则消费者条件等待
如果缓冲区不是空的,就消费,释放互斥锁

3、只有生产者条件等待,消费者负责唤醒

生产者拿到互斥锁后,检查缓冲区的产品数量:
如果缓冲区是满的,则生产者条件等待
如果缓冲区不是满的,就生产,释放互斥锁

消费者拿到互斥锁后,检查缓冲区的产品数量:
如果缓冲区不是空的,就消费,然后发送信号唤醒生产者生产,释放互斥锁

第1种的代码实现

第2,3两种是子集,只需要去除对应代码即可

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

// 生产者消费者的数量 
#define CONSUMER 5
#define PRODUCER 5
// 缓冲区最大数目 
#define BUFFER_SIZE 10

pthread_mutex_t g_mutex;
pthread_cond_t g_cond;

typedef struct st_buffer_t {
    int buf[BUFFER_SIZE]; // 缓冲区 
    int count; // 剩余产品的数量 
	int in; // 生产者放置产品的位置
	int out; // 消费者取产品的位置	
} buffer_t;

buffer_t g_buff = {{0}, 0, 0, 0};

// 打印缓冲情况
void print()
{
	for (int i = 0; i < BUFFER_SIZE; i++) {
		printf("%d ", g_buff.buf[i]);
	}
	printf("\n");
}

void* producer(void *arg)
{
	int producer_id = *(int *)arg;
    printf("Producer %d starting.\n", producer_id);
    while (1) {
    	// 用sleep可以降低生产和消费的速度,便于观察
    	sleep(1);

        pthread_mutex_lock(&g_mutex);
        // 加上while防止线程错误唤醒 
        while (g_buff.count == BUFFER_SIZE) {
        	// 走到这里wait时会把互斥锁释放掉
        	pthread_cond_wait(&g_cond, &g_mutex);
		}

    	// 生产 
    	g_buff.in %= BUFFER_SIZE;
        g_buff.buf[g_buff.in] = 1;
        printf("Prodcuer %d produce 1 in %d: ", producer_id, g_buff.in);
        print();
        g_buff.in++;
        g_buff.count++;

        // 生产后发送信号唤醒消费者线程
        pthread_cond_signal(&g_cond);
        pthread_mutex_unlock(&g_mutex);
    }
    return NULL;
}

void* consumer(void *arg)
{
	int consumer_id = *(int *)arg;
    printf("Consumer %d starting.\n", consumer_id);
    while (1) {
    	// 用sleep可以降低生产和消费的速度,便于观察
    	sleep(1);

        pthread_mutex_lock(&g_mutex);
        // 加上while防止线程错误唤醒 
        while (g_buff.count == 0) {
        	// 走到这里wait时会把互斥锁释放掉
        	pthread_cond_wait(&g_cond, &g_mutex);
		}

		// 消费 
        g_buff.out %= BUFFER_SIZE;
        printf("Consumer %d consume %d in %d: ", consumer_id, g_buff.buf[g_buff.out], g_buff.out);
        g_buff.buf[g_buff.out] = 0;
        print();
        g_buff.out++;
        g_buff.count--;
        
        // 消费后发送信号唤醒生产者线程
        pthread_cond_signal(&g_cond);
        pthread_mutex_unlock(&g_mutex);
    }
    return NULL;
}

int main(int argc, char *argv[])
{
	// 初始化互斥锁和条件变量 
	pthread_mutex_init(&g_mutex, NULL);
    pthread_cond_init(&g_cond, NULL);
	pthread_t consumers[CONSUMER];
	pthread_t producers[PRODUCER];
	int consumers_id[CONSUMER];
	int producers_id[PRODUCER];
	// 创建生产者和消费者的线程 
	for (int i = 0, j = 0; i < CONSUMER || j < PRODUCER; i++, j++) {
		if (j < PRODUCER) {
			producers_id[j] = j;
			pthread_create(&producers[j], NULL, producer, (void *)&producers_id[j]);
		}
		if (i < CONSUMER) {
			consumers_id[i] = i;
			pthread_create(&consumers[i], NULL, consumer, (void *)&consumers_id[i]);
		}
	}
	for (int i = 0, j = 0; i < CONSUMER || j < PRODUCER; i++, j++) {
		if (j < PRODUCER) {
			pthread_join(producers[j], NULL);
		}
		if (i < CONSUMER) {
			pthread_join(consumers[i], NULL);
		}
	}
	// 销毁互斥锁和条件变量 
    pthread_mutex_destroy(&g_mutex);
    pthread_cond_destroy(&g_cond);
    return 0;
}

二、使用互斥锁和同步信号量实现

思路:

两个信号量,一个empty表示可生产产品的数量,一个full表示已生产产品的数量
生产者wait empty减一后,拿到互斥锁,生产,释放互斥锁,post full加一
消费者wait full减一后,拿到互斥锁,消费,释放互斥锁,post empty加一

PS:一般推荐两个保持先同步再互斥,这样可以使得互斥区代码更短,有效提高并发性。

代码实现

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

// 生产者消费者的数量 
#define CONSUMER 5
#define PRODUCER 5
// 缓冲区最大数目 
#define BUFFER_SIZE 10

pthread_mutex_t g_mutex;
sem_t g_empty_sem;
sem_t g_full_sem;

typedef struct st_buffer_t {
    int buf[BUFFER_SIZE]; // 缓冲区 
    int count; // 剩余产品的数量 
	int in; // 生产者放置产品的位置
	int out; // 消费者取产品的位置	
} buffer_t;

buffer_t g_buff = {{0}, 0, 0, 0};

// 打印缓冲情况
void print()
{
	for (int i = 0; i < BUFFER_SIZE; i++) {
		printf("%d ", g_buff.buf[i]);
	}
	printf("\n");
}

void* producer(void *arg)
{
	int producer_id = *(int *)arg;
    printf("Producer %d starting.\n", producer_id);
    while (1) {
    	// 用sleep可以降低生产和消费的速度,便于观察
    	sleep(1);

		// 等待empty信号量大于0
		sem_wait(&g_empty_sem);
        pthread_mutex_lock(&g_mutex);

    	// 生产 
    	g_buff.in %= BUFFER_SIZE;
        g_buff.buf[g_buff.in] = 1;
        printf("Prodcuer %d produce 1 in %d: ", producer_id, g_buff.in);
        print();
        g_buff.in++;
        g_buff.count++;

        pthread_mutex_unlock(&g_mutex);
        // 增加full信号量的值 
        sem_post(&g_full_sem);
    }
    return NULL;
}

void* consumer(void *arg)
{
	int consumer_id = *(int *)arg;
    printf("Consumer %d starting.\n", consumer_id);
    while (1) {
    	// 用sleep可以降低生产和消费的速度,便于观察
    	sleep(1);

		// 等待empty信号量大于0 
		sem_wait(&g_full_sem);
        pthread_mutex_lock(&g_mutex);

		// 消费 
        g_buff.out %= BUFFER_SIZE;
        // 可以通过g_buff.buf[g_buff.out]的值来判断消费者是否消费对了,该值必须为1 
        printf("Consumer %d consume %d in %d: ", consumer_id, g_buff.buf[g_buff.out], g_buff.out);
        g_buff.buf[g_buff.out] = 0;
        print();
        g_buff.out++;
        g_buff.count--;

        pthread_mutex_unlock(&g_mutex);
        // 增加empty信号量的值 
        sem_post(&g_empty_sem);
    }
    return NULL;
}

int main(int argc, char *argv[])
{
	// 初始化互斥锁和同步信号量
	// empty表示可生产的数量;full表示已生产的数量 
	pthread_mutex_init(&g_mutex, NULL);
	sem_init(&g_empty_sem, 0, BUFFER_SIZE);
	sem_init(&g_full_sem, 0, 0);  
	pthread_t consumers[CONSUMER];
	pthread_t producers[PRODUCER];
	int consumers_id[CONSUMER];
	int producers_id[PRODUCER];
	// 创建生产者和消费者的线程 
	for (int i = 0, j = 0; i < CONSUMER || j < PRODUCER; i++, j++) {
		if (j < PRODUCER) {
			producers_id[j] = j;
			pthread_create(&producers[j], NULL, producer, (void *)&producers_id[j]);
		}
		if (i < CONSUMER) {
			consumers_id[i] = i;
			pthread_create(&consumers[i], NULL, consumer, (void *)&consumers_id[i]);
		}
	}
	for (int i = 0, j = 0; i < CONSUMER || j < PRODUCER; i++, j++) {
		if (j < PRODUCER) {
			pthread_join(producers[j], NULL);
		}
		if (i < CONSUMER) {
			pthread_join(consumers[i], NULL);
		}
	}
	// 销毁互斥锁和条件变量 
    pthread_mutex_destroy(&g_mutex);
	sem_destroy(&g_empty_sem);
	sem_destroy(&g_full_sem);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值