使用互斥锁以后,只有一个线程能访问临界区,剩下的线程都会被挂起,也不知道上一个线程什么时候解锁,要是有人来通知该多好
于是乎我们引入了条件变量,条件变量可以看作是一个等待队列,当其他线程忙完以后,可以主动通知条件变量中正在等待的线程。
目录
5、唤醒条件变量中等待的一个线程 pthread_cond_signal
一、条件变量相关函数
条件变量既然可以看作是一个等待队列,就会有对应的数据类型来表示条件变量(等待队列),Linux给我们提供了这种数据类型 —— pthread_cond_t
既然有条件变量这种数据类型,那就需要“创建条件变量”、“初始化条件变量”,“销毁条件变量”的函数,这些属于准备工作。
除上述说的外,我们还需要有能将线程加入到条件变量的函数、唤醒条件变量中的线程的函数。
1、条件变量创建
方式一:
pthread_cond_t pcond; //声明一个条件变量
pthread_cond_init(&pcond,nullptr); //初始化条件变量
方式二:
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; //声明 + 初始化条件变量
2、条件变量初始化 pthread_cond_init
第一个参数是条件变量的地址,第二个参数是条件变量的属性,这里我们一般设置成nullptr;调用成功返回0,调用失败返回一个错误码
3、条件变量销毁 pthread_cond_destroy
参数是条件变量的地址,表示你要销毁哪个条件变量;调用成功返回0,调用失败返回一个错误码
4、条件变量等待 pthread_cond_wait
当线程调用这个函数的时候,当前线程就会被加入到条件变量中,并阻塞等待。
第一个参数是要加入的条件变量,第二个参数是互斥锁;调用成功返回0,调用失败返回一个错误码
关于为什么会有第二个参数mutex??
调用 pthread_cond_wait 有可能是出现在临界区内部的,而临界区一般又是加锁的,考虑到这种情况,所以一般要传入锁。
此时加入到条件变量的线程是带着锁挂起的!!所以这个函数有两个作用:
1、调用的时候,即线程加入到条件变量,自动释放锁,然后线程挂起。
2、返回的时候,即线程被唤醒,自动竞争锁。获取不到锁,就会被挂起到互斥锁的竞争队列下;获取到锁以后才会继续向下执行。因为线程被唤醒的时候处在临界区中,没有锁就无法访问临界区
5、唤醒条件变量中等待的一个线程 pthread_cond_signal
加入到条件变量中的线程会被挂起,这个函数被调用的时候,让条件变量(等待队列) 中排在队首的线程取消挂起,其实就是唤醒排在队首的线程。
参数就是条件变量的地址,即要唤醒哪个条件变量的队首线程;调用成功返回0,调用失败返回一个错误码
二、一个线程使用条件变量控制其他线程
1、代码实现
现在有一个boss线程,三个员工线程,一开始员工线程加入到条件变量阻塞等待,每经过1s,boss线程给条件变发送信号以后,解除条件变量中一个员工线程的阻塞等待,即让这个员工线程开始工作,这就实现了通过一个boss线程来控制员工线程
=======================条件变量、互斥锁=======================
这两个全局变量要放在最前面,子线程要使用全局变量得时候,一般都是向上查找
===========================主线程===========================
主线程的作用是创建boss线程、员工线程,以及回收boss线程、员工线程,销毁条件变量
===========================BOSS线程===========================
一开始boss线程等待6s,等待“员工线程进入工作间”,之后每隔1s给条件变量发送信号,解除条件变量中的一个线程
===========================员工线程===========================
员工线程加入到条件变量,阻塞等待;收到boss的信号以后,解除挂起,开始工作,工作完以后由于死循环重新加入到条件变量中
注意: 这里的互斥锁mtx暂时不考虑,只是为了填充函数参数
2、测试结果
最开始的6s,boss不发送信号,等待员工线程进入工作间,然后加入到条件变量阻塞等待
之后boss线程给条件变量发送信号,每次只解除一个线程的挂起