Linux 条件变量和信号量分别实现生产者消费者模型

1. 条件变量实现

生产者、消费者各1个。生产者往链表插入结点,消费者从链表删除结点。

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

pthread_cond_t cond;
pthread_mutex_t mutex;

typedef struct _node_t { // 链表结点。
    int data;
    struct _node_t* next;
}node_t;

node_t* head = NULL;

void* producer(void* arg) { // 生产者
    node_t* new = NULL;
    while (1) { // 循环生产
        pthread_mutex_lock(&mutex);
        new = malloc(sizeof(node_t)); // 分配结点空间
        if (NULL == new) {
            printf("malloc node_t failed...\n");
            break;
        }

        memset(new, 0, sizeof(node_t));
        new->data = random() % 100 + 1;
        new->next = NULL;

        // 头插法
        new->next = head;
        head = new;
        printf("生产:%d\n", head->data);
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&cond); // 通知可以消费了

        sleep(1);
    }
    return NULL;
}

void* consumer(void* arg) { // 消费者
    node_t* tmp = NULL;
    while (1) { // 循环消费
        pthread_mutex_lock(&mutex);
        if (NULL == head) {
            // 等待有链表有结点
            pthread_cond_wait(&cond, &mutex);
        } else {
            // 消费第一个结点  
            tmp = head;
            head = head->next;
            printf("消费:%d\n", tmp->data);
            free(tmp);
        }
        pthread_mutex_unlock(&mutex);

    }
    return NULL;
}

int main(int argc, const char* argv[]) {

    pthread_t tid1, tid2;

    pthread_cond_init(&cond, NULL);
    pthread_mutex_init(&mutex, NULL);

    pthread_create(&tid1, NULL, producer, NULL); // 生产者线程
    pthread_create(&tid2, NULL, consumer, NULL); // 消费者线程

    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);

    pthread_cond_destroy(&cond);
    pthread_mutex_destroy(&mutex);

    return 0;
}

运行结果:

 

注意

上述代码,若将生产者线程中的printf打印放在释放mutex之后,如下图:

 则会出现段错误,如下图:

原因:将生产者线程中的printf打印放在释放mutex之后,会出现如下情形:

生产者:lock → 插入1个结点 → unlock → 通知消费者;因为通知了消费者,导致后续的printf打印语句还未执行到,消费者就把刚生产出来的结点消费free掉了,此时生产者想printf访问一个已经被释放的结点,就出现段错误。 因此这里要将printf放在mutex范围内。


2. 信号量实现

生产者2个,消费者4个。生产者往链表插入结点,消费者从链表删除结点。链表最多运行有8个结点。

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

sem_t sem_containers; // 可用容器个数
sem_t sem_goods;  // 可消费资源个数

pthread_mutex_t mutex;

typedef struct _node_t { // 链表结点。
    int data;
    struct _node_t* next;
}node_t;

node_t* head = NULL;

void* producer(void* arg) { // 生产者
    int index = (int)(long)arg;
    node_t* new = NULL;
    while (1) { // 循环生产
        sem_wait(&sem_containers);  // 生产一个,可用容器个数-1
        pthread_mutex_lock(&mutex);
        new = malloc(sizeof(node_t)); // 分配结点空间
        if (NULL == new) {
            printf("malloc node_t failed...\n");
            break;
        }

        memset(new, 0, sizeof(node_t));
        new->data = random() % 100 + 1;
        new->next = NULL;

        // 头插法
        new->next = head;
        head = new;
        printf("生产%d生产了:%d\n", index, head->data);
        pthread_mutex_unlock(&mutex);
        sem_post(&sem_goods); // 生产完了,可消费资源个数+1

    }
    return NULL;
}

void* consumer(void* arg) { // 消费者
    int index = (int)(long)arg;
    node_t* tmp = NULL;
    while (1) { // 循环消费
        sem_wait(&sem_goods); // 消费一个,可消费资源个数-1
        pthread_mutex_lock(&mutex);

        // 消费第一个结点  
        tmp = head;
        head = head->next;
        printf("消费者%d消费了:%d\n", index, tmp->data);
        free(tmp);
        pthread_mutex_unlock(&mutex);
        sem_post(&sem_containers);// 消费完了,可用容器个数+1
        sleep(1);
    }
    return NULL;
}

int main(int argc, const char* argv[]) {

    pthread_t tid[6];

    sem_init(&sem_containers, 0, 8);   // 初始可用容器个数为8
    sem_init(&sem_goods, 0, 0); // 初始可消费资源个数为0
    pthread_mutex_init(&mutex, NULL);

    for (int i = 0; i < 6; i++) {
        if (i < 2) {
            pthread_create(&tid[i], NULL, producer, (void*)(long)i); // 2个生产者线程
        } else {
            pthread_create(&tid[i], NULL, consumer, (void*)(long)i); // 4个消费者线程
        }
    }

    for (int i = 0; i < 6; i++) {
        pthread_join(tid[i], NULL);
    }

    sem_destroy(&sem_containers);
    sem_destroy(&sem_goods);
    pthread_mutex_destroy(&mutex);

    return 0;
}

运行结果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Linux中,可以使用C语言的条件变量和线程来实现消费者-生产者模型。下面是一个简单的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; int count = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_prod = PTHREAD_COND_INITIALIZER; pthread_cond_t cond_cons = PTHREAD_COND_INITIALIZER; void *producer(void *arg) { int item = 0; while (1) { pthread_mutex_lock(&mutex); // 如果缓冲区已满,则等待消费者消费 while (count == BUFFER_SIZE) { pthread_cond_wait(&cond_prod, &mutex); } buffer[count] = item; count++; printf("Producer produced item %d\n", item); // 唤醒消费者线程 pthread_cond_signal(&cond_cons); pthread_mutex_unlock(&mutex); item++; } pthread_exit(NULL); } void *consumer(void *arg) { while (1) { pthread_mutex_lock(&mutex); // 如果缓冲区为空,则等待生产者生产 while (count == 0) { pthread_cond_wait(&cond_cons, &mutex); } int item = buffer[count - 1]; count--; printf("Consumer consumed item %d\n", item); // 唤醒生产者线程 pthread_cond_signal(&cond_prod); pthread_mutex_unlock(&mutex); } pthread_exit(NULL); } int main() { pthread_t producer_thread, consumer_thread; // 创建生产者消费者线程 pthread_create(&producer_thread, NULL, producer, NULL); pthread_create(&consumer_thread, NULL, consumer, NULL); // 等待线程结束 pthread_join(producer_thread, NULL); pthread_join(consumer_thread, NULL); return 0; } ``` 在上面的代码中,生产者线程不断地向缓冲区中生产数据,而消费者线程不断地从缓冲区中消费数据。当缓冲区满时,生产者线程会等待条件变量`cond_prod`,直到有消费者消费数据才会被唤醒。同样,当缓冲区为空时,消费者线程会等待条件变量`cond_cons`,直到有生产者生产数据才会被唤醒。 需要注意的是,在生产者消费者线程之间共享的变量`count`和`buffer`需要进行互斥访问,因此使用了互斥锁`mutex`来保护共享资源的访问。 希望这个示例能帮助你理解如何在Linux中使用C语言的条件变量和线程实现消费者-生产者模型

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的马师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值