pthread 的条件变量
通常而言,阻塞一个线程要使用互斥锁 mutex ,但是在特别情况下则可能需要别的选择
比如,当需要等一个线程的中间执行结果时,如果使用互斥锁,那么只能写一个无限循环,并且在循环内不断加锁、解锁,这显然是一个很不优雅的写法
这时可以使用条件变量pthread_cond
来解决这个问题
在一个线程调用int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex);
阻塞该线程,直至条件满足
而在工作线程中,则使用int pthread_cond_signal(pthread_cond_t *cv);
通知阻塞的线程继续执行
这里的pthread_cond_t
和pthread_mutex_t
本质上都是一个锁,可以看作是在 A 线程阻塞,当 B 线程执行结束后解锁该锁,从而使得 A 线程继续执行
pthread_cond_wait
int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mutex);
会阻塞当前线程,等待其他线程的回应
需要注意的是,这个函数执行结束不代表条件一定满足条件,因为条件变量本身也只是一个锁,并不存在任何判断。同时在多核处理器中,也有可能因为各种问题解锁条件变量1。因此,必须在阻塞的线程上判断条件是否符合,也即阻塞的部分应该类似这个样子:
pthread_mutex_lock(&mutex); while (ok) { pthread_cond_wait(&cv, &mutex); } pthread_mutex_unlock(&mutex);
在这里,因为需要在多个进程中都需要对判断的条件涉及的变量进行读写,因此需要使用一个单独的互斥锁对其进行保护。
这也是为什么阻塞函数必须要传入一个互斥锁:需要一个互斥锁对判断条件进行保护2 3
而这个阻塞函数本身要完成的任务如下:
- 释放互斥锁,以供子线程中使用
- 将条件变量锁加入到队列中,等待触发
- 条件变量锁触发,给互斥锁加锁
pthread_cond_signal 和 pthread_cond_broadcast
int pthread_cond_signal(pthread_cond_t *cv);
会发送信号,解锁该条件变量阻塞的一个线程(具体顺序看调用队列)
int pthread_cond_broadcast(pthread_cond_t *cv);
会发送信号,解锁该条件变量阻塞的所有线程
如果cv
对应的条件变量并没有阻塞线程,那么该函数效果相当于没有执行
同时,通常情况而言,调用该函数前需要判断一个条件是否满足,因此对于该条件需要加锁,并且需要是阻塞的线程传入的互斥锁。但是在实际使用中,如果这个条件涉及的变量只在阻塞的线程和工作线程中用到,那么实际上在工作线程里判断条件没有必要加锁4。当然,加了除了可能会影响性能也没有别的问题