条件变量的执行过程(伪代码)
int pthread_cond_wait(&cond,&wait)
{
int ret = pthread_cond_wait_and unlock(&cond,&wait);//进入阻塞状态后解锁
pthread_mutex_lock(&lock);//再去竞争锁
return ret
}
条件变量为什么要搭配互斥锁
1 条件变量的改变一般是临界资源来完成的,那么修改临界资源首先应该加锁
而线程在条件不满足的情况下要阻塞,等待别人唤醒,
那么在阻塞后一定要把锁放开,等到合适的线程拿到锁去修改临界资源,否则会出现死锁
2 在线程被唤醒后第一件事也应该是争取拿到锁,恢复以前加锁的状态.
否则在执行条件变量成立后的代码也法保证其原子性
所以条件变量和锁是相辅相成的:
条件变量需要锁的保护
锁需要 条件变量成立后,后重新上锁
条件变量为什么要使用while循环判断,而不是if
首先我们应该了解,条件变量是用来阻塞或者唤醒线程的,
阻塞和唤醒是依靠信号机制来处理的,
而条件变量中的条件是我们自己规定的,
也就是说当条件成立后,操作系统给阻塞进程发送信号,唤醒这个进程
那万一操作系统抽风了,随便给进程发送唤醒信号 (此时条件还没成立),
线程醒了之后就会在条件没成立的情况下执行之后的代码
这也是第一点 用if 不能保证bug信号的有效处理, 但是
while()
{
cond_wait(&cond,&mutex);
}
唤醒操作后如果再次竞争到锁,第一件事就是再次进入while 看条件是否成立 能有效杜绝bug信号
2.
生产消费者模型, 生产者生产一个产品,两个消费者 A和B 同时竞争这一个产品
假定是两个消费者都先后拿到锁,发现没有商品然后睡眠放锁
此时必定生产者得到锁,生产一个产品,然后退出
当用broadcat或者signal唤醒消费者时,
假设消费者A先拿到了锁 消费完产品之后退出
B后拿到锁, 因为是while 循环,又会执行一次判断 发现没有产品, 接着进入等待
如果 此时条件判断改成if 消费者b在wait被唤醒后 会直接执行接下来的消费语句
会发现生产者之只生产了1个产品,唤醒了两个消费者,两个消费者同时”消费”了一个产品
错误!
验证代码
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
pthread_mutex_t mutex;
pthread_cond_t cond;
int flag = 0;
void* product(void* msg)
{
char* who = (char*) msg;
pthread_mutex_lock(&mutex);
flag = 1;
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mutex);
return NULL;
}
void* consume(void* msg)
{
char* who = (char*) msg;
pthread_mutex_lock(&mutex);
while(flag == 0)
pthread_cond_wait(&cond, &mutex);
printf(" %s said : i got the flag\n", who);
flag = 0;
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL);
pthread_t producter, consumer_1, consumer_2;
pthread_create(&producter, NULL, product, "producter");
pthread_create(&consumer_1, NULL, consume, "consumer_1");
pthread_create(&consumer_2, NULL, consume, "consumer_2");
pthread_join(producter, NULL);
pthread_join(consumer_1, NULL);
pthread_join(consumer_2, NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
}