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;
}
  • 1
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是生产者-消费者问题的代码示例,利用信号量和进程来实现: ``` #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <semaphore.h> #define MAX_BUFF_SIZE 10 // 缓冲区大小 #define MAX_PROD_NUM 20 // 生产者生产的最大数量 // 缓冲区 char buffer[MAX_BUFF_SIZE] = {0}; int in = 0, out = 0; // 信号量 sem_t empty_sem; // 空槽位数量 sem_t full_sem; // 已填充槽位数量 sem_t mutex_sem; // 互斥信号量 // 生产者线程 void* producer(void* arg) { int num = *(int*)arg; for (int i = 0; i < num; i++) { sem_wait(&empty_sem); // 获取空槽位 sem_wait(&mutex_sem); // 获取互斥权 buffer[in] = 'A' + i % 26; printf("producer %lu: produce %c\n", pthread_self(), buffer[in]); in = (in + 1) % MAX_BUFF_SIZE; sem_post(&mutex_sem); // 释放互斥权 sem_post(&full_sem); // 填充一个槽位 sleep(1); // 等待一段时间 } pthread_exit(NULL); } // 消费者线程 void* consumer(void* arg) { int num = *(int*)arg; for (int i = 0; i < num; i++) { sem_wait(&full_sem); // 获取已填充槽位 sem_wait(&mutex_sem); // 获取互斥权 printf("consumer %lu: consume %c\n", pthread_self(), buffer[out]); buffer[out] = 0; out = (out + 1) % MAX_BUFF_SIZE; sem_post(&mutex_sem); // 释放互斥权 sem_post(&empty_sem); // 释放一个空槽位 sleep(1); // 等待一段时间 } pthread_exit(NULL); } int main() { int prod_num = 6, cons_num = 4; // 初始化信号量 sem_init(&empty_sem, 0, MAX_BUFF_SIZE); sem_init(&full_sem, 0, 0); sem_init(&mutex_sem, 0, 1); // 创建生产者线程 pthread_t prod_tid, prod_tid2; pthread_create(&prod_tid, NULL, producer, (void*)&prod_num); pthread_create(&prod_tid2, NULL, producer, (void*)&prod_num); // 创建消费者线程 pthread_t cons_tid, cons_tid2; pthread_create(&cons_tid, NULL, consumer, (void*)&cons_num); pthread_create(&cons_tid2, NULL, consumer, (void*)&cons_num); // 等待线程结束 pthread_join(prod_tid, NULL); pthread_join(prod_tid2, NULL); pthread_join(cons_tid, NULL); pthread_join(cons_tid2, NULL); // 销毁信号量 sem_destroy(&empty_sem); sem_destroy(&full_sem); sem_destroy(&mutex_sem); return 0; } ``` 代码说明: 1. 定义全局变量 `buffer` 作为缓冲区,变量 `in` 和 `out` 分别表示插入和读取缓冲区的位置。 2. 定义三个信号量 `empty_sem`, `full_sem`, `mutex_sem`,分别表示空槽位数量、已填充槽位数量和互斥信号量。 3. 在生产者消费者函数中,使用 `sem_wait` 和 `sem_post` 操作信号量,来实现同步和互斥。 4. 程序中创建 2 个生产者线程和 2 个消费者线程,它们分别使用 `pthread_create` 函数创建,等待线程结束使用 `pthread_join` 函数。 5. 程序结束时使用 `sem_destroy` 函数销毁信号量。 这个程序使用信号量和互斥信号量来解决生产者-消费者问题,保证了生产者消费者之间的同步和互斥,可以避免竞态条件和死锁的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值