通常,条件变量和互斥锁搭配使用。
条件变量是用来弥补互斥锁的不足的。在我们使用互斥锁的时候,往往是为了等待某个线程修改完数据之类的条件满足后,释放互斥锁。这个过程中,其他线程会尝试竞争锁,竞争失败后才会进入阻塞队列,这个过程还是会消耗CPU的资源。
而使用条件变量以后,当条件满足的时候,会发送信号去唤醒等待这个条件的线程,去加锁,再判断条件是否满足。
至于为什么要再判断条件,这个一会再提。
总之,使用条件变量以后,线程就不需要主动去竞争锁,而是等待着被唤醒,这样可以提高CPU是利用率。
1.初始化条件变量
与互斥锁一样,有静态和动态。
1.1静态初始化
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
1.2动态初始化
int pthread_cond_init(pthread_cond_t *cond, pthread_cond_addr_t *cond_attr);
参数分别是传入条件变量的地址,以及条件变量属性的结构体,如果默认属性填NULL即可。
2.条件变量的等待
2.1阻塞等待
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
2.2超时等待
int pthread_cond_timewait(pthread_cond_t *cond, pthread_mutex_t *mutex, const timespec *abstime);
等待的过程有三步:(1)释放互斥锁;(2)等待被唤醒;(3)被唤醒,锁上互斥锁。这三步是分开的,并非原子操作。
3.条件变量唤醒
3.1唤醒一个等待该条件的线程
int pthread_cond_signal(pthread_cond_t *cond);
3.2唤醒所有等待该条件的线程
int pthread_cond_broadcast(pthread_cond_t *cond);
4.销毁条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
4.虚假唤醒
例如我们有一个生产者消费者程序,消费者从一个队列里面取数据,我们在等待条件变量的时候应该这么写。
pthread_mutex_lock(&mutex);
while(qu.empty()){
pthread_cond_wait(&cond, &mutex);
}
/*
取数据操作
*/
pthread_mutex_unlock(&mutex);
其中,为什么要用一个while循环呢?
这就是有关虚假唤醒的问题。
当你同时唤醒多个线程的时候,有多个线程,其中一部分线程已经把队列里面的数据都取完了,而剩下的线程虽然也被唤醒了,但是队列已经空了,所以这是虚假唤醒。