问题描述
生产者消费者问题:
(英语: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;
}