生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。 . 要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。
代码实现:(线程版&条件变量&互斥锁)
#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <pthread.h> struct msg { struct msg *next; int num; }; struct msg *head; //数据结构使用的时队列 pthread_cond_t has_product = PTHREAD_COND_INITIALIZER; //判断是否有产品 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; void *consumer(void *p) { struct msg *mp; for (;;) { pthread_mutex_lock(&lock); while (head == NULL) { //头指针为空,说明没有节点,处理有多个消费者的问题 pthread_cond_wait(&has_product, &lock); } mp = head; head = mp->next; //模拟消费掉一个产品 pthread_mutex_unlock(&lock); printf("-Consume ---> %d\n", mp->num); free(mp); sleep(rand() % 5); } } void *producer(void *p) { struct msg *mp; while (1) { mp = malloc(sizeof(struct msg)); mp->num = rand() % 1000 + 1; //模拟生产一个产品 printf("-Produce ---> %d\n", mp->num); pthread_mutex_lock(&lock); mp->next = head; head = mp; pthread_mutex_unlock(&lock); pthread_cond_signal(&has_product); //将等待在该条件变量上的一个线程唤醒,叫醒阻塞的进程 sleep(rand() % 5); } } int main(int argc, char *argv[]) { pthread_t pid, cid; srand(time(NULL)); pthread_create(&pid, NULL, producer, NULL); pthread_create(&cid, NULL, consumer, NULL); pthread_join(pid, NULL); pthread_join(cid, NULL); return 0; }线程&信号量版本:
/*信号量实现 生产者 消费者问题*/ #include <stdlib.h> #include <unistd.h> #include <pthread.h> #include <stdio.h> #include <semaphore.h> #define NUM 10 int queue[NUM]; //全局数组实现环形队列 sem_t blank_number, product_number; //空闲空间信号量, 产品信号量 void *producer(void *arg) { int i = 0; while (1) { sem_wait(&blank_number); //p操作 生产者将空格子数--,为0则阻塞等待 queue[i] = rand() % 1000 + 1; //生产一个产品 printf("----Produce---%d\n", queue[i]); sem_post(&product_number); //v操作 将产品数++ i = (i+1) % NUM; //借助下标实现环形 sleep(rand()%3); } } void *consumer(void *arg) { int i = 0; while (1) { sem_wait(&product_number); //消费者将产品数--,为0则阻塞等待 printf("-Consume---%d\n", queue[i]); queue[i] = 0; //消费一个产品 sem_post(&blank_number); //消费掉以后,将空闲空间数++ i = (i+1) % NUM; sleep(rand()%3); } } int main(int argc, char *argv[]) { pthread_t pid, cid; sem_init(&blank_number, 0, NUM); //初始化空闲空间信号量为10 sem_init(&product_number, 0, 0); //产品数为0 pthread_create(&pid, NULL, producer, NULL); pthread_create(&cid, NULL, consumer, NULL); pthread_join(pid, NULL); pthread_join(cid, NULL); sem_destroy(&blank_number); sem_destroy(&product_number); return 0; }