在多线程同步互斥的应用场景下,通常会用到pthread_cond_wait()和pthread_cond_signal()函数。那么这两个函数到底是如何保证互斥同步的呢?
为了对上面的问题有个直观的了解,可以从下面的问题着手。下面两段这些程序有什么bug么?
a. 等待条件的线程执行的代码:
Thread
1:
{
……………………..
pthread_cond_wait(cv,
mutex);
do_worker_task();
……………………..
}
b.唤醒等待线程的代码:
{
pthread_mutex_lock(mutx);
pthread_cond_signal(cv)
pthread_mutex_unlock(mutex);
}
要知道上面代码问题在哪,关键是必须对pthread_cond_wait()有深入的了解。为了对这个函数又直观的了解,下面给出了pthread_cond_wait(cv,
mutex)正常执行过程中的几个重要子步骤伪码:
pthread_cond_wait(cv,
mutex) 实际做的事情:
a.
pthread_mutex_unlock(mutex);
b.
do_real_cond_wait(cv); // 可能在这阻塞直到其他线程调用pthread_cond_signal(cv)或者 pthread_cond_broadcast(cv),才会往下执行
c.
pthread_mutex_lock(mutex);
结合在多处理器、多线程、高并发的环境下,上面三个步骤就显得缺一不可,才能保证尽可能地互斥:
步骤a:保证其他线程此后也有等待、申请条件变量的同等机会
步骤b:在条件变量还没就绪的情况下,执行真正的阻塞
步骤c:当前线程得到条件变量之后,阻止其他等待条件变量的线程继续往下执行,而自己可以往下执行。
那么问题来了,考虑步骤c,如果单独一句pthread_cond_wait(cv,
mutex)是无法确保线程尽可能互斥的,这就是为何在执行pthread_cond_wait()之前要求有pthread_mutex_lock(mutx)的原因了,因此正常申请条件变量的线程的流程应该如下:
Thread
1:
{
pthread_mutex_lock(mutx);
pthread_cond_wait(cv,
mutex);
do_worker_task();
pthread_mutex_unlock(mutex);
}
至于释放条件变量,理解了上边的内部机制之后,其实在释放条件变量之前就不需要申请锁再释放锁了。
这里要特别提到的一点是,按照上面正确申请条件变量的的流程,仍然存在多个等待条件变量的线程同时执行步骤b出等待,此时如果有信号释放,就会唤醒多个线程。这也就是为什么手册上说pthread_cond_signal(cv)至少唤醒一个在等待cv的线程(可以计数资源为1)的原因了。
参考文档和手册:
man
pthread_cond_signal