先来看下《Linux高性能服务器编程》中对条件变量的描述:
上述话可以总结为:
多线程中某一个线程依赖于另外一个线程对共享数据的改变时,就可以使用条件变量!
用消费者生产者的来理解条件变量的话,就很好理解了
取自于:条件变量基本概念和原理_相信并热爱着的技术博客_51CTO博客_条件变量_条件变量原理
上述这位大哥说到的,在消费者生产者角度来解释这个东西,就可以看的很透彻了,需要时唤醒,不需要时,就进行睡眠。
下面来看一下条件变量会用到的函数以及头文件:
#include <pthread.h> //头文件,和线程创建函数所使用头文件一模一样
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr); //条件变量的初始化
第一个参数为条件变量的指针,第二个参数为条件变量的属性,一般置为NULL
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);//等待条件变量满足,等到唤醒之后,就会启动该线程,否则该线程一直处于阻塞状态
第一个参数是条件变量的指针,第二个参数是互斥锁(互斥锁和条件变量一般一起用,为什么呢?因为互斥锁只有两种状态,锁定和没锁定,而条件变量则是可以通过线程之间发送信号来让线程进行等待或是唤醒,弥补了互斥锁单一的锁定方式,因此互斥锁和条件变量一般在一起使用)。
int pthread_cond_signal(pthread_cond_t *cond); //唤醒单个线程
int pthread_cond_broadcast(pthread_cond_t *cond); //唤醒所有等待的线程
int pthread_cond_destroy(pthread_cond_t *cond); //条件变量的销毁
上面这三个函数的参数都是条件变量的指针
下面来看一段代码,它的功能是主线程往buff中写入数据,在写的过程中两个子线程在等待,然后主线程写入完成之后,子线程完成打印。然后当主线程输入end之后,进程结束!
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
pthread_mutex_t mutex; //创建互斥锁
pthread_cond_t cond; //创建条件变量
char buff[128] = {0};
void* funa(void* arg)
{
while( 1 )
{
//等待条件可用
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
if ( strncmp(buff,"end",3) == 0 )
{
break;
}
//打印buff
printf("funa buff=%s\n",buff);
}
}
void * funb(void* arg)
{
while( 1 )
{
//等待条件可用
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex); //互斥锁加锁之后等待主线程的信号
pthread_mutex_unlock(&mutex);
if ( strncmp(buff,"end",3) == 0 )
{
break;
}
//打印buff
printf("funb buff=%s\n",buff);
}
}
int main()
{
pthread_cond_init(&cond,NULL);
pthread_mutex_init(&mutex,NULL);
pthread_t ida, idb;
pthread_create(&ida,NULL,funa,NULL);
pthread_create(&idb,NULL,funb,NULL);
while( 1 )
{
//键盘获取数据-buff
fgets(buff,128,stdin);
//通知线程 signal broadcast
if ( strncmp(buff,"end",3) == 0 )
{
pthread_mutex_lock(&mutex); //加锁
pthread_cond_broadcast(&cond); //输入end时唤醒所有线程 让大家一起结束 因为线程之间都是平级的,主线程的退出不影响子线程的运行(主要是下面还有join函数在等待子线程)
pthread_mutex_unlock(&mutex); //解锁
break;
}
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond); //唤醒其中一个子线程
pthread_mutex_unlock(&mutex);
}
pthread_join(ida,NULL);
pthread_join(idb,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
exit(0);
}
上述代码中两个子线程代码一毛一样,那么来看看子线程中的这三行代码:
到底有几把锁?
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
上述三行代码中有几个锁?先思考一下
有两个锁,听我来给你捋一下,随着两个子线程函数创建的那一刻,两个子线程进行对互斥锁的竞争,其中必然只有一个线程能够拿到锁,没拿到锁的那个线程就会进入阻塞,拿到锁的哪一个线程进行枷锁,然后到上述提出来的代码的第二行,pthread_cond_wait(&cond,&mutex);参数中会传入一个互斥锁,到这行的时候,锁其实就已经解开了,然后就会wait等待主线程中信号来唤醒该线程,另外一个线程也是如此,刚开始没拿到锁,但是在另外一个子线程解锁之后,那个子线程会wait等待唤醒,这时候刚开始没拿到锁的线程会重复前面的操作,然后进入wait等待唤醒,但是这唤醒的时候,两个子线程都在等待,这时候唤醒的线程的时候,会随机唤醒其中一个线程,这时候pthread_cond_wait(&cond,&mutex);出这行代码的时候会进行枷锁,只有一个子线程能被唤醒,而且是随机唤醒!没被唤醒的线程就一直在那wait等待唤醒