1、 int pthread_cond_init(pthread_cond_t *restrict cond,
const pthread_condattr_t *restrict attr);//用自定义属性初始化一个条件变量
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;//用默认属性静态初始化一个条件变量
函数介绍:pthread_cond_destroy为销毁一个条件变量,“销毁”这个词其实不大贴切,本函数的真正效果是,使得条件变量cond变为未初始化的状态。下面的各种关于条件变量的API,是不允许使用“未初始化的条件变量”的,否则会导致不可预知的结果。一个被“销毁”的条件变量,可以被重新初始化。
在某时刻,如果没有任何线程被该条件变量cond堵塞,那么使用pthread_cond_destroy函数来“销毁”这个条件变量,是个安全操作,但是,如果如果某个线程正在堵塞地等待这个条件变量,这时如果“销毁”了这个cond,那么会引发不可预知的结果。
只有cond变量本身可以被用作同步。换句话说,不允许使用cond变量的副本,传给这些api:pthread_cond_wait(), pthread_cond_timedwait(), pthread_cond_signal(), pthread_cond_broadcast(), and pthread_cond_destroy() ,否则会导致不可预知的结果。
对于一个已经初始化过的条件变量,进行二次初始化,会导致不可预知的后果。
对于静态分配的条件变量,可以用PTHREAD_COND_INITIALIZER这个宏对它进行静态初始化,效果相当于用
pthread_cond_init(&cond, NULL)进行初始化。
pthread_mutex_t *restrict mutex,
const struct timespec *restrict abstime);
当条件变量为某个值时,该函数会堵塞。
该函数会原子性地释放互斥量(实参mutex),并且本线程会堵塞。这里所谓的“原子性地”的意思是:另一个线程总是原子性地先访问mutex再访问条件变量cond。也就是说:假设有一个线程A,即将被堵塞,A已经释放了互斥锁,这时线程B获得了这个互斥锁,那么线程B获取互斥锁之后,调用pthread_cond_broadcast()或pthread_cond_signal()的效果,就好像是在线程A被堵塞之后调用的似的(尽管此时A还没有被堵塞)。
一旦本函数成功返回,那么实参互斥锁mutex将会被锁住,也即,该mutex被本线程所持有,此时别的线程都无法获得该mutex。
当使用条件变量时,总是有一个这样的布尔值,这个值跟每一个条件变量相关,当这个布尔值true时,线程才会继续下去。线程可能从该函数中被假唤醒。由于从该函数中返回时,无法指示出关于这个布尔值的信息,所以,在本函数返回时,应重新判断一下这个布尔量的值。
这两个wait函数需要用到一个mutex和一个条件变量cond,这个mutex通过互斥来保护cond变量,如果并行的多个wait函数使用了不同mutex来保护cond,结果是不可预知的。也即,当等待条件变量cond时,这个cond必须与惟一的mutex上绑定,当条件变量成立(解除堵塞)后,才能结束这个动态绑定。
这两个函数都是“线程取消点”,关于取消点的概念,可参考另一篇博文《linux多线程相关的API-(3)--线程取消cancel与清理push/pop》
返回值:成功则返回0,失败则直接返回错误码,而不是设置errno。
这个函数pthread_cond_wait可以分解为这种形式,来帮助我们来理解它的工作过程:
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex)
{
pthread_mutex_unlock(mutex);//解锁,这样别的线程才可以可以修改cond
wait(cond);//立即进入睡眠,直到别的线程执行了pthread_cond_signal或者pthread_cond_broadcast,才能执行下一行
pthread_mutex_lock(mutex);//加锁
}
由上述分解过程可知,在使用这两个API时,应在这两个函数前后用和pthread_mutex_unlock(mutex)给包起来:形如:
pthread_mutex_lock(&mutex);//加锁
while(myval != 3)
pthread_cond_wait(&cond, &mutex);//本函数内部会解锁、加锁
pthread_mutex_unlock(&mutex);//解锁
3、 int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast可以唤醒所有被cond条件变量堵塞的线程;
pthread_cond_signal可以唤醒至少一个被cond条件变量堵塞的线程(我认为应该是最多一个,但手册上写的就是最少一个,我不理解);
如果被cond堵塞的线程不止一个,那么这些线程的唤醒顺序取决于调度策略。当这两个函数唤醒那些被cond堵塞的线程时,被唤醒的线程将会从pthread_cond_wait、或者pthread_cond_timedwait函数中返回,此时被唤醒的线程就会把互斥锁带走(持有互斥锁mutex)。这些被唤醒的线程会按照调度策略来争夺互斥锁,好像是每一个被唤醒的线程都调用了pthread_mutex_lock这个函数似的。
不管某线程当前是否持有mutex,它都可以调用pthread_cond_signal、pthread_cond_broadcast。当然,如果你希望调度行为是可预测的、是可控的,那么你应当在调用pthread_cond_signal、pthread_cond_broadcast之前,把互斥锁锁住(持有这个互斥锁)。
如果没有任何线程被cond给堵塞,这时调用pthread_cond_signal、pthread_cond_broadcast,也没啥事,不会产生什么坏结果的。
返回值:成功则返回0,否则直接返回错误码,而不是设置errno。
应用实例:标准的使用步骤如下:
应用场景描述:定义一个所有线程均可读写的全局变量uint8 cmd,主线程实时修改cmd的值,
当cmd=1时,线程1可以运行,否则线程1睡眠;
当cmd=2时,线程2可以运行,否则线程2睡眠;