version 1.0
一个顾客,一个老板,各为一个线程。
/*************************************************************************
*> version : 1.0
*> Author: giturtle
*> Describe: 实现条件变量的基本使用
吃面的前提是:有人做面
如果没有现成的面,等待老板做出来
老板做出来面,就要唤醒顾客
老板不会做太多的面,只会提前做一碗面
如果已经有面做出来,但是没人吃,不会再做第二碗,进行等待
顾客吃碗面之后,要求再来一碗,进行唤醒(老板)
*************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
int have_noodle = 1;
pthread_cond_t cond;
pthread_mutex_t mutex;
void *thr_boss(void * arg) {
while(1) {
pthread_mutex_lock(&mutex);
//若面没有卖出去,则等待
if (have_noodle == 1) {
pthread_cond_wait(&cond, &mutex); //等待(死等)
//pthread_cond_wait 中集合了解锁后挂起的操作(原子操作)
//有可能还没来得及挂起就已经有人唤醒--白唤醒--导致死等
}
//面被人吃了,要再做一碗
printf("boss:produce an noodle +1\n");
have_noodle += 1;
//此时面做出来了~端走端走(唤醒顾客)
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thr_customer(void *arg) {
while(1) {
pthread_mutex_lock(&mutex);
//若此时没有现成的面,就等老板做好
if (have_noodle == 0) {
pthread_cond_wait(&cond, &mutex); //等待
}
//如果有面了就可以吃面了
printf("customer:delicious~ noodle -1\n");
have_noodle -= 1;
//吃完没有吃饱~ 再来一碗 (唤醒老板)
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond);
}
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid1, tid2;
int ret;
//条件变量 初始化
pthread_cond_init(&cond, NULL); //或 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_init(&mutex, NULL);
ret = pthread_create(&tid1, NULL, thr_boss, NULL);
if (ret != 0) {
printf("boss create error\n");
return -1;
}
ret = pthread_create(&tid2, NULL, thr_customer, NULL);
if (ret != 0) {
printf("customer create error\n");
return -1;
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
//销毁条件变量
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex);
return 0;
}
运行结果
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
boss:produce an noodle +1
customer:delicious~ noodle -1
version 2.0
多个厨师,多个食客。
-
因为有可能多个线程(食客)等待在条件变量上,其中一个完成吃面动作解锁,此时第二个正好获取到锁资源,进行吃面操作,造成一碗面被多人进食的错误情景。
所以:将回调函数中的if
判定条件改为while
循环判定,进行吃面操作之前先判断是否还有面。 -
pthread_cond_wait()
唤醒的是所有等待在条件变量上的线程,但有可能被唤醒的这个线程也是一个做面的线程,因为已经有面,条件不满足而陷入等待,导致死等。
所以:线程有多少角色,就应该有多少个条件变量。分别等待,分别唤醒。
/*************************************************************************
*> version : 2.0
*> Author: giturtle
*> Describe: 处理一碗面被多人食用的情况
*************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
int have_noodle = 1;
pthread_cond_t boss;
pthread_cond_t customer;
pthread_mutex_t mutex;
void *thr_boss(void * arg) {
while(1) { //循环判断,有可能第一个吃面者已经改为 0 了
pthread_mutex_lock(&mutex);
//若面没有卖出去,则等待
while(have_noodle == 1) {
pthread_cond_wait(&boss, &mutex);
//1. 解锁 -》 2. 休眠 -》 3. 被唤醒后加锁
}
//面被人买了,要再做一碗
printf("boss:produce an noodle +1\n");
have_noodle += 1;
pthread_cond_signal(&customer);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thr_customer(void *arg) {
while(1) {
pthread_mutex_lock(&mutex);
//若没有现成的面,等老板做好
while(have_noodle == 0) {
pthread_cond_wait(&customer, &mutex);
}
printf("customer:delicious~ noodle -1");
have_noodle -= 1;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&boss);
}
return NULL;
}
int main(int argc, char *argv[]){
pthread_t tid1, tid2;
int ret;
pthread_cond_init(&boss, NULL);
pthread_cond_init(&customer, NULL);
pthread_mutex_init(&mutex, NULL);
int i = 0;
for (i = 0; i < 2; i++) {
ret = pthread_create(&tid1, NULL, thr_boss, NULL);
if (ret != 0) {
printf("boss error\n");
return -1;
}
}
for (i = 0; i < 2; i++) {
ret = pthread_create(&tid2, NULL, thr_customer, NULL);
if (ret != 0) {
printf("customer error\n");
return -1;
}
}
pthread_join(tid1, NULL);
pthread_join(tid2, NULL);
//销毁条件变量
pthread_cond_destroy(&boss);
pthread_cond_destroy(&customer);
pthread_mutex_destroy(&mutex);
return 0;
}