条件变量(生产者消费者问题)

问题描述

生产者消费者问题:(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
解决方案: 这里我们使用C语言多线程模拟生产者和消费者问题同时采用条件变量的方法解决该问题。当数据栈中没有数据时,消费者阻塞在条件变量上等待生产者通知其消费;而生产者则会不断的向数据栈中写入数据,并且每写入一次都会通知消费者进行消费。

条件变量介绍

条件 变量顾名思义就是当条件满足时会执行,在本案例中当数据栈有数据时通过生成者通知消费者有数据可以消费(即条件满足),消费者去消费。条件变量的使用也需要结合互斥锁。

  • 互斥锁的定义
pthread_mutex_t mutex;
  • 条件变量的定义
pthread_cond_t cold;
  • 初始化互斥锁和条件变量
pthread_mutex_init(&lock,NULL);
pthread_cond_init(&cond,NULL);

这两个函数中的最后一个参数均是对应变量的属性,无可传入NULL;

  • 条件变量的使用
pthread_cond_wait(&cond,&lock);

调用该函数时要明白两个问题:第一个是当该方法被调用时会解锁互斥锁并阻塞; 第二个是当该条件满足时会重新加锁互斥锁并执行其下方代码

  • 通知条件满足
pthread_cond_signal(&cond);

调用该方法通知阻塞在该条件上的程序条件满足可以执行,该方法会通知至少一个阻塞在该条件上的变量;

  • 互斥锁和条件变量的销毁
pthread_mutex_destroy(&lock);
pthread_cond_destroy(&cond);

其它数据结构定义说明

  • 数据栈数据元定义
typedef struct student
{
    int age;
    char name[30];
    struct student *next;
}Student;

定义一个学生的数据结构模仿生产和消费的数据。其中数据结构中有int类型的年龄,char类型的名字和指向下一个结构体的student指针。

  • 打印学生信息
void printf_stu(Student *stu){
    printf("Thread:%lu-----Student %s's age is %d\n",pthread_self(),stu->name,stu->age);
}
  • 打印错误
void pthread_error(char *err){
    printf("%s\n",err);
}

该函数用于线程创建失败后的错误信息显示;

完整代码示例

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

typedef struct student
{
    int age;
    char name[30];
    struct student *next;
}Student;

pthread_mutex_t lock;
pthread_cond_t cond;
Student *stu_header;

void printf_stu(Student *stu){
    printf("Thread:%lu-----Student %s's age is %d\n",pthread_self(),stu->name,stu->age);
}

void pthread_error(char *err){
    printf("%s\n",err);
}

void *producer(void *arg){
    int i = 0;
    while (1)
    {
        pthread_mutex_lock(&lock);
        Student *stu;
        stu = (Student*)malloc(sizeof(stu));
        strcpy(stu->name,"Leo");
        stu->age = ++i;
        stu->next = stu_header;
        stu_header = stu;
        pthread_mutex_unlock(&lock);
        pthread_cond_signal(&cond);
        sleep(1);
    }
    
    return NULL;
}

void *consumer(void *arg){

    while (1)
    {
        pthread_mutex_lock(&lock);
        while(stu_header==NULL){
            pthread_cond_wait(&cond,&lock);//调用的时候解锁,唤起的时候要加锁
        }
        printf_stu(stu_header);
        stu_header=stu_header->next;
        pthread_mutex_unlock(&lock);
        sleep(5);
    }
    
    return NULL;
}

int main(int argc, char const *argv[])
{
    int ret;
    pthread_t producer_tid,consumer_tid,consumer_tid_a;

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

    ret = pthread_create(&producer_tid,NULL,producer,NULL);
    if(ret!=0){
        pthread_error("pthread create producer");
    }
    ret = pthread_create(&consumer_tid,NULL,consumer,NULL);
    if(ret!=0){
        pthread_error("pthread create consumer");
    }

    ret = pthread_create(&consumer_tid_a,NULL,consumer,NULL);
    if(ret!=0){
        pthread_error("pthread create consumer");
    }

    pthread_join(producer_tid,NULL);
    pthread_join(consumer_tid,NULL);
    pthread_join(consumer_tid_a,NULL);

    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);

    return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的生产者消费者模型的示例代码,使用了线程和条件变量: ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define BUFFER_SIZE 10 int buffer[BUFFER_SIZE]; int count = 0; int in = 0; int out = 0; pthread_mutex_t mutex; pthread_cond_t not_full; pthread_cond_t not_empty; void *producer(void *arg) { int item = 0; while (1) { item = rand() % 100 + 1; // 产生一个随机数 pthread_mutex_lock(&mutex); while (count == BUFFER_SIZE) { pthread_cond_wait(&not_full, &mutex); // 缓冲区已满,等待消费者消费 } buffer[in] = item; printf("Producer produced item %d, buffer[%d]\n", item, in); in = (in + 1) % BUFFER_SIZE; count++; pthread_mutex_unlock(&mutex); pthread_cond_signal(&not_empty); // 唤醒等待消费的线程 sleep(1); } return NULL; } void *consumer(void *arg) { int item = 0; while (1) { pthread_mutex_lock(&mutex); while (count == 0) { pthread_cond_wait(&not_empty, &mutex); // 缓冲区为空,等待生产者生产 } item = buffer[out]; printf("Consumer consumed item %d, buffer[%d]\n", item, out); out = (out + 1) % BUFFER_SIZE; count--; pthread_mutex_unlock(&mutex); pthread_cond_signal(&not_full); // 唤醒等待生产的线程 sleep(1); } return NULL; } int main() { pthread_t tid_producer, tid_consumer; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&not_full, NULL); pthread_cond_init(&not_empty, NULL); pthread_create(&tid_producer, NULL, producer, NULL); pthread_create(&tid_consumer, NULL, consumer, NULL); pthread_join(tid_producer, NULL); pthread_join(tid_consumer, NULL); pthread_mutex_destroy(&mutex); pthread_cond_destroy(&not_full); pthread_cond_destroy(&not_empty); return 0; } ``` 在这个示例中,缓冲区是一个循环队列,使用变量 `count` 记录缓冲区中的元素个数,使用变量 `in` 和 `out` 分别记录下一个生产者放入元素的位置和下一个消费者取出元素的位置。 生产者线程不断产生随机数,并将其放入缓冲区中。如果缓冲区已满,则线程进入等待状态,等待消费者线程取出元素后唤醒。当生产者放入元素后,唤醒等待消费的线程。 消费者线程不断从缓冲区中取出元素,并打印出来。如果缓冲区为空,则线程进入等待状态,等待生产者线程放入元素后唤醒。当消费者取出元素后,唤醒等待生产的线程。 需要注意的是,在使用条件变量时,线程必须先获得互斥锁才能等待条件变量,否则会出现竞态条件。当线程等待条件变量时,会自动释放互斥锁,等待条件变量时也是需要判断条件的,如果条件不符合,则线程会继续等待,直到条件符合才会被唤醒。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值