linux多线程之生产者消费者问题
什么叫做生产者消费者问题?
生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
解决方法
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号量法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形
开发流程图示:
生产者: 消费者:
1. mutex_lock()加锁 1. mutex_lock()加锁
2. while 循环判断库存 2. while 循环库存判断
已满(真):cond_wait()等待 已空(真):cond_wait()等待
未满(假):循环结束 未空(假):循环结束
3. 生产产品 3. 消费产品
4. if 判断消费条件(库存为1)是否满足 4. if 判断生产条件(库存为最大库存-1)是否满足
满足:signal()/broadcast()广播 满足:signal()/broadcase()广播
5. mutex_unlock()解锁 5. mutex_unlock()解锁
6. 选择性使用sleep()暂停生产,让出CPU 6. 选择性使用sleep()暂停消费,让出CPU
为何不使用 if 判断库存,而使用 while 循环判断库存?
生产者:当库存已满,调用cond_wait()等待,某时刻接收到条件满足信号,
立即去加锁,但是锁被其他生产者拿走了,待其他生产者生产完产品释放锁,
随后竞争到锁时库存变为满状态,此时就应该重新判断库存状态。
这里使用 while 循环就能实现cond_wait()前后都进行库存判断的效果。
消费者:当库存已空,调用cond_wait()等待,某时刻接收到条件满足信号,
立即去加锁,但是锁被其他消费者拿走了,待其他消费者消费完产品释放锁,
随后竞争到锁时库存变为空状态,此时就应该重新判断库存状态。
这里使用 while 循环就能实现cond_wait()前后都进行库存判断的效果。
实现
产品通过链表方式存储源码查看:https://gitee.com/oneGotoOne/linuxLearn/blob/master/11/demo.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define maxProductNumber 5
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t condPro = PTHREAD_COND_INITIALIZER;
pthread_cond_t condCon = PTHREAD_COND_INITIALIZER;
typedef struct msg {
int pro_array[maxProductNumber];
int size;
} stor;
stor product;
void *producer(void *arg)
{
while (1) {
pthread_mutex_lock(&mutex);
while(product.size == maxProductNumber) {
pthread_cond_wait(&condPro, &mutex);
}
product.pro_array[product.size] = (rand() % 200) + 1;
product.size++;
printf("%lu:produce a product:%d Total products:%d\n", \
pthread_self(), product.pro_array[product.size - 1], product.size);
if (product.size == 1) {
pthread_cond_signal(&condCon);
}
pthread_mutex_unlock(&mutex);
sleep(rand()%5);
}
pthread_exit(NULL);
}
void *consumer(void *arg)
{
while (1) {
pthread_mutex_lock(&mutex);
while (product.size == 0) {
pthread_cond_wait(&condCon, &mutex);
}
printf("%lu:consume a product:%d Total products:%d\n", \
pthread_self(), product.pro_array[product.size], --product.size);
if (product.size == maxProductNumber -1) {
pthread_cond_signal(&condPro);
}
pthread_mutex_unlock(&mutex);
sleep(rand()%5);
}
pthread_exit(NULL);
}
int main()
{
int ret = 0;
pthread_t producerID;
pthread_t consumerID;
product.size = 0;
srand(time(NULL));
ret = pthread_create(&producerID, NULL, producer, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create:producer error ret:%s \n", strerror(ret));
exit(-1);
}
ret = pthread_create(&consumerID, NULL, consumer, NULL);
if (ret != 0) {
fprintf(stderr, "pthread_create:consumer error ret:%s \n", strerror(ret));
exit(-1);
}
pthread_join(producerID, NULL);
pthread_join(consumerID, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&condCon);
pthread_cond_destroy(&condPro);
return 0;
}